servicecomb-java-chassis
servicecomb-java-chassis copied to clipboard
connection was closed问题指引
日志中出现类似错误比较常见,有些场景属于配置错误,有些场景属于性能问题,有些场景属于无法彻底解决的问题。这个 issue 主要帮助更好的定位和分析类似问题。
类似问题通过 label 做了标记: 查看
原理性介绍和定位技巧相关文章
快速失败和重试 : 介绍了随机故障发生的原理,以及在超时时间设置很短(需要快速失败的)的场景下的方案建议。 性能问题分析和调优: 这类问题一般伴随性能问题发生。 介绍了如何分析性能问题,以及如何收集性能问题有关的日志。
关于 HTTP 连接
TCP和HTTP连接的一些原理性介绍 微服务内部采用HTTP连接的情况下, 一般会复用连接,如果一个连接长时间不使用,则会回收。 常见的配置项有:
配置项 | 含义 |
---|---|
servicecomb.rest.client.connection.idleTimeoutInSeconds | 客户端连接闲置时间。当客户端连接在规定的时间内,没有任何数据读入和写入,则会触发客户端主动关闭连接。配置参考 |
servicecomb.rest.client.connection.timeoutInMillis | 连接建立的超时时间。如果在规定的时间内,没有成功建立连接,那么会关闭连接。配置参考 |
servicecomb.request.timeout | 请求超时时间。客户端发出请求后,会等待服务端响应,如果在规定的时间内没有收到响应,那么客户端请求失败,并且会关闭连接。配置参考 |
servicecomb.rest.server.connection.idleTimeoutInSeconds | 服务端连接闲置时间。当服务端连接在规定的时间内,没有任何数据读入和写入,则会触发服务端主动关闭连接。当服务端使用vert.x作为HTTP服务器的时候有效,如果使用其他web容器,比如Tomcat,相关配置请参考对应web容器的文档。配置参考 |
建议 5<= servicecomb.request.timeout (毫秒 -> 秒) <= servicecomb.rest.client.connection.idleTimeoutInSeconds <= servicecomb.rest.server.connection.idleTimeoutInSeconds - 10 以避免随机故障和连接管理导致的随机失败概率。
关于随机故障
为了更好的理解随机故障,建议运行一下最简单的性能测试用例,并打开metrics观察各个环节的耗时情况。 这里只提供一些简单的结论和随机故障发生的原理描述。
- 在端到端平均时延为1ms左右的情况下,仍然有一些请求的最大时延超过700ms。超时随机故障在高并发场景下,是必然会出现的一个故障,超时时间设置越短,发生故障的概率越高。
- 时延抖动最厉害的环节有
- 获取连接(getConnect):不可控因素包括网络故障、请求流量的突发性、大规模连接的管理。从metrics看出,平均获取连接时间小于1ms,但是最大可能等待100ms。
- 等待响应(waitResponse):不可控因素包括服务端请求排队和派发的随机性;网络排队和传输的随机性。从Server和Client的metrics可以看出,Server的最大时延是100ms左右,而Client等待响应的最大时延有超过500ms的。
- 唤醒等待线程(wakeConsumer):收到响应后,需要唤醒等待线程,等待线程进行后续处理。不可控因素主要体现为线程调度,CPU本身是被竞争的资源,CPU资源无法按照一个请求已经处理了多长时间进行分配。从metrics看出,平均唤醒等待线程时间小于1ms,但是最大可能等待200ms。
- 锁竞争:Client调用Server的handlersReq也有100ms左右的波动,这是因为启用了流控功能,流控功能存在锁。
- CPU竞争、内存竞争:对于一些纯CPU计算的逻辑,也存在时延波动,这些波动相对较小。比如Server端的执行逻辑,只是echo结果,平均时延可以忽略不计,最大时延也存在60ms的波动。这些波动的主要原因是CPU分时计算、垃圾回收等。可以看出,只要涉及到资源竞争:网络、线程池、连接池、CPU、锁等,就可能出现一个请求的处理被延迟。这个延迟的时间在平均时延小于1ms的情况下,可能高达500ms。 延迟的时间和并发请求数、TPS有很强的关联性。
欢迎补充其他类似问题定位和处理经验
针对这个问题,在vertx未升级的版本其实存在一个废弃连接的使用导致此问题发生 这种场景下和性能其实没有关系 很多时候在这个场景下去从性能入手找问题就走偏了
这里提供一个比较容易触发的场景:
- 使用servicecomb vertx升级之前的版本,我这里使用的2.1.5版本,同时通信模式都是使用的 servlet
- 客户端开启一个定时任务,比如每隔{time}s发送一个请求到服务端, 这个{time}与服务端tomcat设置的断开连接时间要一直或相近比较容易触发
我们通过抓包,发现了导致此问题的原因:
客户端复用连接池,在TCP连接四次挥手期间发送请求,会导致请求失败
解决办法
- 请求量少的可以用短连接
- 我们请求量较大,通过重试机制去解决此问题
复现demo
客户端:
// 起5个线程发请求 线程数根据maxpoolsize和核心数配置
public void run(ApplicationArguments args) throws Exception {
for (int i = 0; i < 5; i++) {
new Thread(new SendThread()).start();
}
}
//发送请求
public void testSsl() throws InterruptedException {
RestTemplate restTemplate = RestTemplateBuilder.create();
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss:SSS");
for(int i = 0; i< 10; i++){
if(i == 0 ){
Thread.sleep(20000);
}
String url = "cse://test-provider2/provider/tomcat/hello/";
// 间隔时间和服务端tomcat的连接保持时间相关
Thread.sleep(2000 + new Random().nextInt(3) + 1);
UUID uuid = UUID.randomUUID();
System.out.println(formatter.format(new Date())+uuid);
ResponseEntity<String> responseEntity =
restTemplate.getForEntity(url + uuid, String.class);
System.out.println("get result "+formatter.format(new Date()) + " " + responseEntity.getBody());
}
}
服务端:
// 接口
@GetMapping("/hello/{name}")
public String sayHello(@PathVariable("name") @Length String name, InvocationContext invocationContext) throws InterruptedException {
return name+", hello";
}
// tomcat配置
server:
port: 8888
tomcat:
accesslog:
directory: D:\code\CSE\demo-2
enabled: true
pattern: '%t %a %A %m %U%q %s %D %I %B'
max-connections: 1000
accept-count: 200
threads:
max: 200
connection-timeout: 2000
问题现象:CSE客户端发起HTTPS调用时,偶现报错:SslHandshakeTimeoutException: handshake timed out after 10000ms 可能出现问题的原因分析: 1、CPU资源不足 首次SSL建连协商密钥使用非对称加密,CPU资源占用较高,CPU资源不足时可能存在导致响应变慢。 2、网络故障 可能的原因包括:网络闪断、网络延迟增大、网络连接不足。 3、服务进程性能问题 FullGC导致的JVM STW。 问题分析验证: 因xxx服务的频繁FullGC导致Tomcat周期性无法响应,部分FullGC时间达到10秒以上
注册中心默认的idle timeout是60s,刚好和客户端默认值一样, 因此建议和注册中心的配置使用下面的值:
servicecomb.service.registry.client.timeout.idle: 25 # 实际客户端idle为 25 * 2
servicecomb.service.registry.instance.healthCheck.interval: 15
servicecomb.service.registry.instance.pull.interval: 15
老版本bug链接:
- Keepalive时间也可能会影响1.x: https://github.com/apache/servicecomb-java-chassis/issues/3607
- Keepalive时间也可能会影响2.x:https://github.com/apache/servicecomb-java-chassis/issues/3136
2.8.10 对相关参数默认值进行了调整, 参考: https://github.com/apache/servicecomb-java-chassis/releases/tag/2.8.10
是否可以将idleTimeoutInSeconds的值设置为0,关闭该检测能力,仅使用keepAlive来管理连接
客户端将servicecomb.rest.client.connection.idleTimeoutInSeconds设置为0,且服务端是基于Tomcat管理连接的,将servicecomb.rest.client.connection.keepAliveTimeoutInSeconds设置为60s
在发送一个请求之后,使用wireshark进行抓包,发现该连接在维持了60s后,被关闭
当进行一个长时间的请求时,连接一直维持,直到request.timeout或者请求返回
补充一个非常特殊的场景,该场景会触发 Connection reset 等异常。
场景描述: 在容器场景, A 调用 B。 当重启 B 所在的节点或者容器的时候, 如果B重启后,保持容器 IP 、端口不变,但是 A 又没有感知到B重启, 那么A的连接池会有B的未关闭的连接信息;而B重启了,B没有连接信息,如果这个时候, A访问B,就会报告 Connection reset等错误。 这种错误在虚机、物理机也会发生,但是容器场景更容易出现。
这种场景如果业务的请求频率不高,并且idletimeout等超时时间设置的比较长, 就会持续很长时间的错误,直到所有不可用的连接关闭重连。
@liubao68 请问服务端超时时间如何配置