servicecomb-java-chassis icon indicating copy to clipboard operation
servicecomb-java-chassis copied to clipboard

connection was closed问题指引

Open liubao68 opened this issue 3 years ago • 9 comments

日志中出现类似错误比较常见,有些场景属于配置错误,有些场景属于性能问题,有些场景属于无法彻底解决的问题。这个 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有很强的关联性。

欢迎补充其他类似问题定位和处理经验

liubao68 avatar Oct 08 '21 07:10 liubao68

针对这个问题,在vertx未升级的版本其实存在一个废弃连接的使用导致此问题发生 这种场景下和性能其实没有关系 很多时候在这个场景下去从性能入手找问题就走偏了

这里提供一个比较容易触发的场景:

  • 使用servicecomb vertx升级之前的版本,我这里使用的2.1.5版本,同时通信模式都是使用的 servlet
  • 客户端开启一个定时任务,比如每隔{time}s发送一个请求到服务端, 这个{time}与服务端tomcat设置的断开连接时间要一直或相近比较容易触发

我们通过抓包,发现了导致此问题的原因:

客户端复用连接池,在TCP连接四次挥手期间发送请求,会导致请求失败

huishou

解决办法

  1. 请求量少的可以用短连接
  2. 我们请求量较大,通过重试机制去解决此问题

复现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

five111 avatar Oct 11 '21 07:10 five111

问题现象:CSE客户端发起HTTPS调用时,偶现报错:SslHandshakeTimeoutException: handshake timed out after 10000ms 可能出现问题的原因分析: 1、CPU资源不足 首次SSL建连协商密钥使用非对称加密,CPU资源占用较高,CPU资源不足时可能存在导致响应变慢。 2、网络故障 可能的原因包括:网络闪断、网络延迟增大、网络连接不足。 3、服务进程性能问题 FullGC导致的JVM STW。 问题分析验证: 因xxx服务的频繁FullGC导致Tomcat周期性无法响应,部分FullGC时间达到10秒以上

fanjiwang1992 avatar Oct 21 '22 08:10 fanjiwang1992

注册中心默认的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

liubao68 avatar Mar 01 '23 10:03 liubao68

老版本bug链接:

  1. Keepalive时间也可能会影响1.x: https://github.com/apache/servicecomb-java-chassis/issues/3607
  2. Keepalive时间也可能会影响2.x:https://github.com/apache/servicecomb-java-chassis/issues/3136

liubao68 avatar May 25 '23 10:05 liubao68

2.8.10 对相关参数默认值进行了调整, 参考: https://github.com/apache/servicecomb-java-chassis/releases/tag/2.8.10

liubao68 avatar Oct 24 '23 08:10 liubao68

是否可以将idleTimeoutInSeconds的值设置为0,关闭该检测能力,仅使用keepAlive来管理连接

yanghao605 avatar Feb 04 '24 09:02 yanghao605

客户端将servicecomb.rest.client.connection.idleTimeoutInSeconds设置为0,且服务端是基于Tomcat管理连接的,将servicecomb.rest.client.connection.keepAliveTimeoutInSeconds设置为60s 在发送一个请求之后,使用wireshark进行抓包,发现该连接在维持了60s后,被关闭 image 当进行一个长时间的请求时,连接一直维持,直到request.timeout或者请求返回

yanghao605 avatar Feb 04 '24 09:02 yanghao605

补充一个非常特殊的场景,该场景会触发 Connection reset 等异常。

场景描述: 在容器场景, A 调用 B。 当重启 B 所在的节点或者容器的时候, 如果B重启后,保持容器 IP 、端口不变,但是 A 又没有感知到B重启, 那么A的连接池会有B的未关闭的连接信息;而B重启了,B没有连接信息,如果这个时候, A访问B,就会报告 Connection reset等错误。 这种错误在虚机、物理机也会发生,但是容器场景更容易出现。

这种场景如果业务的请求频率不高,并且idletimeout等超时时间设置的比较长, 就会持续很长时间的错误,直到所有不可用的连接关闭重连。

liubao68 avatar Feb 20 '24 09:02 liubao68

@liubao68 请问服务端超时时间如何配置

ghosts321 avatar Apr 15 '24 08:04 ghosts321