高并发下Finagle服务报ChannelClosedException上游地址不可用问题咨询
问题复现说明
我们的Scala/Thrift服务运行在Java8环境,依赖的Twitter Finagle版本为2.12-18.10.0。服务在单请求或低并发场景下运行正常,高并发场景下偶现如下异常:
com.twitter.finagle.ChannelClosedException: ChannelException at remote address: hostname:9091 from service: calcservice-thrift. Remote Info: Upstream Address: Not Available, Upstream id: Not Available, at org.apache.thrift.TApplicationException.readFrom(TApplicationException.java:131)
客户端测试代码如下:
log.info("started"); IntStream.range(0,500) .parallel() .forEach(i->{ try { //remote call to finagle service } catch(Exception e){ log.error("error " ,e); } }); log.info("end");
服务端随机关闭通道的核心原因
- Finagle默认连接池阈值触发:高并发下连接数触及服务端默认的连接上限、请求并发上限,服务端为避免资源耗尽,主动回收闲置或超额连接,若此时客户端正好使用被回收的连接发起请求,就会抛出通道关闭异常
- 服务端过载熔断触发:短时间内并发请求量超出服务端处理能力,请求队列积压触发Finagle默认的过载保护机制,主动关闭过载的连接释放资源
- 版本已知缺陷:Finagle 2.12-18.10.0版本存在连接状态判断的偶发bug,高并发场景下会误将活跃连接判定为闲置连接进行回收
- 传输限制触发:若业务存在大体积请求,超出Finagle默认的Thrift帧最大长度限制,服务端会主动关闭传输异常的连接,故障偶现是因为只有部分请求超过阈值
- 操作系统层面限制:服务端所在机器的文件描述符上限、TCP连接回收参数配置不合理,高并发下连接被操作系统内核主动关闭
可行修复方案
- 调整Finagle服务端配置:放宽连接池、并发请求限制,调大闲置连接超时时间,示例配置参考:
// 服务端配置调整 Thrift.server .withSessionPool.maxSize(1024) // 调大最大连接数 .withSessionPool.maxIdleTime(5.minutes) // 调长闲置连接回收时间 .withMaxConcurrentRequests(2048) // 调大最大并发请求数 .withAdmissionControl.concurrencyLimit(maxWaiters = 1024) // 调大等待队列长度 - 配置客户端重试策略:针对幂等请求,配置Finagle客户端对
ChannelClosedException这类连接异常进行自动重试,示例配置参考:// 客户端配置调整 Thrift.client .withRetryPolicy( RetryPolicy.tries( 3, RetryPolicy.TimeoutAndWriteExceptionsOnly .or(RetryPolicy.equalTo(classOf[ChannelClosedException])) ) ) - 升级Finagle版本:如果依赖升级不受限制,建议升级到2.12-22.7.0及以上的稳定版本,修复已知的连接回收逻辑缺陷
- 调整操作系统网络配置:调大服务端机器的文件描述符上限(
ulimit -n 65535),优化TCP keepalive、TIME_WAIT回收等参数,避免内核主动关闭正常业务连接 - 调大Thrift帧限制:如果业务存在大请求场景,同步调整服务端和客户端的Thrift帧最大长度,示例配置:
.withThriftFramedTransport.maxFrameSize(10 * 1024 * 1024) // 调整帧上限为10MB - 优化客户端并发逻辑:JDK并行流默认使用公共ForkJoin池,并发度不可控,容易突发打满服务端资源,建议替换为自定义线程池,控制请求并发速率,做削峰处理。
内容的提问来源于stack exchange,提问作者Ashish Garg
相关产品推荐
相关产品推荐

