目录
背景、现象
紧急处理
排查过程、现场。
问题排查、定位
处理方案
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
- 接口long-url 优化。
- 压测。压出系统瓶颈,优化服务。
- 根据压测数据做服务限流、熔断、降级等。