记录SpringCloudGateway的一个隐藏问题

简介: 线上生产环境中,一个SCG接口偶发性出现“Connection reset by peer”错误。排查发现问题是由于Netty的HTTP客户端连接池保持了已由服务端关闭的连接。解决方案是配置连接池以在超时后回收连接(超时时间应小于Tomcat的连接超时时间),并考虑将连接池获取策略从FIFO改为LIFO,以减少使用无效连接的可能性。通过修改Spring Cloud Gateway的HTTP客户端连接池配置和添加JVM启动参数可以实现这一修复。

问题现象:

今天遇到一个SCG的问题,线上生产环境在请求一个接口的时候,偶发性的会出现报错

io.netty.channel.unix.Errors$NativeIoException: readAddress(..) failed: Connection reset by peer

排查过程

看到这个错误的第一反应是连接被服务端关闭了,以为是服务端的问题,但是经过测试,不通过网关直接访问该服务时,是没有任何问题的,因此将目光聚焦到了SCG上,通过排查SCG的日志,发现确实是在网关层出现该问题,具体问题报错如上。

针对这个错误,去百度了一下,发现是因为scg底层netty的问题导致的,参考文章链接:https://blog.csdn.net/qq_22903677/article/details/128990516

然后在netty的github issues上也找到了对应的问题:https://github.com/reactor/reactor-netty/issues/938

大概意思如下:

netty为了节省开销,搞了一个http client的连接池,这个连接池维护了后端tomcat的连接,如果下次有请求过来,会先查连接池里是否有可复用的连接,如果有直接使用该连接

问题原因

问题就出现在这个连接池这,netty搞的这个连接池默认是不释放连接的,从连接池取用连接的策略默认是FIFO先入先出,也就是会取最早的连接,而tomcat连接是有最大空闲连接时间限制的,也就是说tomcat连接可能已经关闭了,但是netty的连接池里还保留这个连接,当netty使用已关闭的连接的时候就会报这个错误。

如下图:

解决办法

那么解决办法很明显了:

  1. 给netty的httpclient连接池增加限制,在连接超时后回收连接,此连接超时时间需小于tomcat连接超时时间
  2. 修改netty的httpclient连接池的获取连接策略,由FIFO改为LIFO,即每次获取最新的连接,最大限度保证获取的连接不是无效连接

具体配置如下:

修改连接池配置:

spring:
  cloud:
    gateway:
      httpclient:
        pool:
          type: fixed
          max-idle-time: 5000
          max-connections: 200
          acquire-timeout: 45000

修改连接池获取策略为LIFO:

增加jvm启动参数 -Dreactor.netty.pool.leasingStrategy=lifo

注:本人只配置了第一个修改连接池属性的参数,并未配置lifo,也可以生效;个人感觉仅配置lifo策略应该还会出现该问题,重点的配置仍然是连接池配置。

PS

一点小探究:

连接池的max-connections参数原理:

因为新增了http clinent的最大连接数的配置,不确定这个配置是否合理,因此看下这个参数的默认值,避免生产环境出现问题:

这个配置是通过SCG的HttpClientProperties类接受的

向下找到pool类

可以看到maxConnections的默认值为ConnectionProvider.DEFAULT_POOL_MAX_CONNECTIONS;

int DEFAULT_POOL_MAX_CONNECTIONS = Integer.parseInt(System.getProperty("reactor.netty.pool.maxConnections", "" + Math.max(Runtime.getRuntime().availableProcessors(), 8) * 2));

而默认最大连接数是跟处理器数量有关系,默认为16(8处理器一下),因此最大连接数设置为200不算小。

目录
相关文章
|
1月前
表单项validator的校验tip未撑起高度,导致遮盖下边项的内容
表单项validator的校验tip未撑起高度,导致遮盖下边项的内容
18 0
表单项validator的校验tip未撑起高度,导致遮盖下边项的内容
|
24天前
|
UED
禁止手机侧滑返回上一个页面的三种方法
禁止手机侧滑返回上一个页面的三种方法
22 0
|
10月前
scrollIntoView()定位元素显示导致页面上移解决方法?
scrollIntoView()定位元素显示导致页面上移解决方法?
278 0
|
1月前
关于实现点击父元素点击后显示子元素,但点击子元素不消失的方法(阻止事件传递)
关于实现点击父元素点击后显示子元素,但点击子元素不消失的方法(阻止事件传递)
24 0
|
10月前
隐藏页面的元素的方式
隐藏页面的元素的方式
MPAndroidChart_并列柱状图,及如何实现点击隐藏掉不需要的条目。
在比赛中遇到了MP的并列柱状图,看网上对这方面记录并不是很多,所以今天就做一个教程吧。
164 0
MPAndroidChart_并列柱状图,及如何实现点击隐藏掉不需要的条目。
|
安全 Android开发
如何向前一个Fragment回传信息?
我们使用Activity的时候,可以通过startActivityForResult来启动一个新的Activity,然后在新的Activity中可以用setResult来向前一个Activity回传一些结果信息,前一个Activity会在onActivityResult中得到这个信息。 那么当我们使用Fragment的形式来作为页面的时候呢?我们知道可以使用setArguments向后面的Fragment传递信息,但是如何回传结果信息? 作者:BennuCTech 链接:https://juejin.cn/post/7033953422357299231 来源:稀土掘金 著作权归作者所有。商
117 0
|
C# 编译器
C#隐藏方法
不能删除基类的任何成员,但可以用与基类成员相同的成员来屏蔽基类成员 屏蔽数据成员:派生类中声明名称和类型相同的成员 屏蔽函数成员:在派生类中声明新的带有函数签名的成员 让编译器知道:添加new关键字,否则会警告pet petpu.
630 0
|
Web App开发 测试技术