关于锁:消失的死锁从-JSF-线程池满到-JVM-初始化原理剖析-京东云技术团队
一、问题形容在一次上线时,依照失常流程上线后,察看了线上报文、接口可用率十分钟以上,未出现异常状况,后果在上线一小时后忽然收到jsf线程池耗尽的报警,并且该利用一共有30台机器,只有一台机器呈现该问题,迅速下线该机器的jsf接口,复原线上。而后开始排查问题。 报错日志信息:[WARN]2023-04-10 18:03:34.847 [ - ][] |[JSF-23002]Task:java.util.concurrent.FutureTask@502cdfa0 has been reject for ThreadPool exhausted! pool:200, active:200, queue:0, taskcnt: 2159[BusinessPool#:][JSF-SEV-WORKER-225-T-8] 二、问题剖析1、呈现问题起因:a)因为只有一台机器呈现线程池耗尽,其余机器均失常运行。所以第一工夫判断是否为有大量流量负载不平衡导致; b)业务代码存在并发锁; c)业务代码解决工夫较长; d)拜访数据源(如DB、redis)变慢; 排查接口流量UMP监控,依照机器纬度看,发现每个机器流量是平衡的,排除a)项; 排查业务量大的接口UMP KEY监控,依照机器纬度看,失常机器和异样机器耗时基本一致,并于平常统一,无较大差别,排除c)项; 排查数据库监控,无慢sql,读写均无耗时较长的状况,排除d)项; 综上,只剩下b)项,确认问题起因是代码存在并发锁,故开始排查日志及业务代码。 2、依据已确认的起因排查思路:1)down下dump文件,发现极多JSF线程处于RUNNABLE状态,并且堆栈处于SerializersHelper类 "JSF-BZ-22000-223-T-200" #1251 daemon prio=5 os_prio=0 tid=0x00007fd15005c000 nid=0xef6 in Object.wait() [0x00007fce287ac000] java.lang.Thread.State: RUNNABLE at com.jd.purchase.utils.serializer.helper.SerializersHelper.ofString(SerializersHelper.java:79) at com.jd.ldop.pipe.proxy.OrderMiddlewareCBDExportServiceProxy.getAddress(OrderMiddlewareCBDExportServiceProxy.java:97) at com.jd.ldop.pipe.proxy.OrderMiddlewareCBDExportServiceProxy.findOrder(OrderMiddlewareCBDExportServiceProxy.java:211)依据堆栈信息排查代码,发现代码会初始化一个自定义的序列化工厂类:SerializerFactory 并且此时初始化时会打印日志: log.info("register: {} , clazz : {}", serializer.getCode(), serializer.getClass().getName());故依据此日志关键字排查初始化加载日志,发现失常机器都加载了两个序列化对象,只有出问题的那个机器只加载了这一个序列化对象。 于是问题初步定位到出问题的机器初始化ProtoStuffSerializer这个类时失败。 初始化此类时static代码块为: static { STRATEGY = new DefaultIdStrategy(IdStrategy.DEFAULT_FLAGS);}2)开始排查为什么初始化这个类会失败 因为不同机器存在初始化胜利和失败的独立性,首先思考jar包是否抵触 a)于是发现这里存在jar抵触,然而将抵触jar排除后,屡次重启机器后发现仍然存在此ProtoStuffSerializer初始化失败状况。 b)存在死锁,然而失常逻辑下,存在死锁的话,应该所有机器都会存在此类情况,然而此时大略只有5%的几率呈现死锁,并且排查jstack发现200个线程都卡在获取ProtoStuffSerializer。 ...