天天看点

spring httpInvoker 导致的大量tomcat线程阻塞问题排查

目录

背景、现象

紧急处理

排查过程、现场。

问题排查、定位

处理方案

TODO

  • 背景、现象

3天内,nginx 504告警、大量请求504。客户反馈用户打不开页面。

  • 紧急处理

单台重启服务器容器,重启后问题解决。

        之后几天又出现了 (监控显示流量并未出现大量升高、突增的现象)、监控显示不是ES和MYSQL的瓶颈后,想到先紧急扩容服务器,容器数量又4台增加到10台。线上问题解决。

  • 排查过程、现场。

    1、cat监控监控发现大量接口URL响应时间40s以上。

    2、cat监控 查看线程栈 发现ActiveThread 大量上升。HttpThread 大量上升。GC CPU 等监控均正常。

    3、打开耗时较长logview 发现是httpInvoker调用一个报表服务耗时过长,但是代开报表服务确没有发现耗时较长的URL 和SOA客户端打点调用的消耗时间不一致。继续查看DB、ES均未发现集群异常、慢查询等。

    4、打开网络监控 、发现服务器大量连接close_wait (因为服务请求超时,Nginx代理主动断开连接导致的)

    spring httpInvoker 导致的大量tomcat线程阻塞问题排查
  • 问题排查、定位

根据cat定位到耗时代码,SOA调用,但是服务端并未出现long url , 猜想是SOA的问题,处理线程有阻塞,(判断基本是阻塞引起的)我们使用的spring 的 httpinvoker

       查看代码有配置   

HttpComponentsHttpInvokerRequestExecutor 的 connectTimeout、readTimeout (后来发现仅这两项设置是不完全的) 也设置了 httpClient的最大连接池的连接数 。 继续查找阻塞原因,(由于重启、和扩容容器线上并没有保留栈的现场)。不过可以从CAT查看当时线程栈(cat每分钟会dump一次线程栈,PS:dump会短暂进入safePoint 可以结合实际业务选择是否去除) 堆栈分析httpInvoker线程 发现catalina线程waitting的阻塞httpclient在从连接池获取连接的地方初步定为是 ConnectionRequestTimeout 没有设置。后继续定为代码发现还有 MaxPerRoute的参数,从代码追踪 HttpRoute具体指的是每个targetHost和端口即为一个router spring invoker默认值是5,由于调用报表服务用的都是同一个域名和端口导致perRouter到达上限,出现大量线程阻塞等待获取链接,又由于没有设置从连接池获取空闲连接的等待时间,默认为0,表示一直等待,所以出现大量activate——thread ,请求到达瓶颈后出现大量504。

  • 处理方案

    1、设置连接池获取连接的等待时间 httpClient.getParams().setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, 10*1000);

    2、设置socket读取数据时间

    httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT,10*1000);

    3、设置MaxPerRoute 结合业务增加该配置值。

    PS:此处发现原生的spring设置这些参数不生效,必须用如上的参数设置才有效。

  • TODO

  1. 接口long-url 优化。
  2. 压测。压出系统瓶颈,优化服务。
  3. 根据压测数据做服务限流、熔断、降级等。