一:触发场景
feign请求第三方服务报错;
请求未结束,关闭了连接
二:原因分析:
参考:https://blog.csdn.net/rickiyeat/article/details/107900585?spm=1001.2014.3001.5506
SCG官方文档有说,设置请求第三方服务的连接超时和读取超时实际上是设置的org.springframework.cloud.gateway.config.HttpClientProperties类属性,接着挖下去,HttpClientProperties其实就是提高配置能力,为初始化reactor.netty.http.client.HttpClient做门面,其实这个配置类和你知道的HttpClient没啥直接关系,它只是模拟出了类似HttpClient该有的一些机制,譬如连接池(使用过HttpClient的老铁在线上出幺蛾子的时候一定也把玩过它的连接/线程池参数)机制,HttpClientProperties里面的pool属性就是设置连接池相关的属性的。
看到这里,你只需要知道,SCG的底层Reactor-Netty会为请求实例创建连接池,以便后面发起请求不用重新创建请求,直接从中获取即可。其实这也就是问题的根因,看下面的时序图你就明白了:
这里使用一个Spring Boot内置Tomcat作为服务提供方,用户通过SCG访问,SCG代理请求。
默认情况下,SCG内部创建的连接是不会被回收的,一直存在于内存中,而Spring Boot内置的Tomcat不一样,默认在20s之后没有数据交互,便会回收掉这个连接,在回收的时候恰巧碰到又来了请求,刚好又在SCG拿到这个连接来尝试请求Tomcat,就会出现这个异常。
所以,不要指望在Reactor-Netty或是SCG中解决这个问题,这需要网关和后端服务配合解决,最大限度不出现这个异常。
三:解决方式
第1步、加入JVM参数:
-Dreactor.netty.pool.leasingStrategy=lifo
第2步、SCG新增配置:
spring:
cloud:
gateway:
httpclient:
pool:
maxIdleTime: 10000(根据需要调整)
第1步将获取连接策略由默认的FIFO变更为LIFO,因为LIFO能够确保获取的连接最大概率是最近刚被用过的,也就是热点连接始终是热点连接,而始终用不到的连接就可以被回收掉,LRU的思想。
第2步是设置空闲请求在空闲多久后会被回收,这样也就可以避免拿到旧连接刚好在请求途中被强行close了,这个时间的设置只要确保比你后端服务的connectTimeout小就行了,这样能够确保SCG回收请求在后端服务回收请求之前,就可以避免掉这个问题。
这样设置后还会偶发这个异常,请排查你的所有后端服务是否connectTimeout都比maxIdleTime大,或者尝试调整maxIdleTime。另外,本身这是个概率性偶发问题,如果你的架构是题主举的这个例子类似,题主这样设置后,几乎看不到这个异常出现了,彻底根除这个顽疾,请看懂时序图再提问题。另外,如果你的架构不太一样,你需要找到你的请求为什么在请求途中被突然关闭的原因,这可能不是Reactor-Netty的问题,而是你的服务的问题。
常规解决:
第一步,每次采用lifo方式获取最近使用来获取http线程池连接
第二步:缩小空闲关闭连接时间,保证每次feig请求结束后,及时关掉即可
第一步,不改一般也能解决问题