Tomcat线程池满引发的登录异常

8 阅读1分钟

上周六的时候前端说登录接口很慢,看了下日志,是登录接口调用其他服务慢,日志如下

get xx from xx duration: 120089ms

看了下机器的负载没啥问题,中间件也都正常,猜测可能是这个同事改了啥,负责这个接口的同事恰好请假了,和他说了下,让他周一排查下。因为前端要用,本着重启万能的方法,把服务重启了,然后登录接口就没问题了(这时候也没想具体原因)。

周一的时候负责接口的同事说他的接口没问题,很快的,说可能是k8s服务之间的调用可能有问题。我想了下,调用用的是feign接口,feign调用时会从nacos拿实例的ip,LoadBalancer 选择一个实例(默认轮询),Feign 使用选中的 IP:Port 发起真实 HTTP 请求,并不会走dns,按理来说是没问题的。负责的同事还是重启了服务,重启后一切安好(这其实就可以判断是这个服务自身有问题)。过了一会,登录又有问题了,我一想,不会是tomcat的线程池满了吧(之前遇到过类似的问题)。

去对应的pod看了下,

jstack -l 9
​
"http-nio-8901-exec-199" #672 daemon prio=5 os_prio=0 cpu=32.09ms elapsed=429.75s tid=0x00007efaa0105950 nid=0x2c8 waiting for monitor entry  [0x00007ef7758d3000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.**.xxWithResult(**ServiceImpl.java:766) -- 业务代码
        - waiting to lock <0x00001000162642b0> org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
        at java.lang.Thread.run(java.base@17.0.11/Thread.java:842)
​
   Locked ownable synchronizers:
        - <0x00001000d3faee58> (a org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker)
        - <0x00001000a6d3f258> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
​

统计了下

root@xserver-b75894866-cn5gv:/app# jstack -l 9 | grep xxWithResult | wc -l
200

果然是tomcat线程池用完了,去看了下代码,是另一个同事在这个方法上加了synchronized,这个方法比较慢,是被一个自动化任务调用了,短时间内调用次数多,一下就把线程池用完了。

回看了之前类似问题的记录,发现还是这个同事写的代码,上次用的是lock,lock没有释放,然后也把线程池用完了,状态是 WAITING ,和 synchronized 锁略有区别。

jstack -l 7 
​
"http-nio-8901-exec-350" #50838 daemon prio=5 os_prio=0 cpu=230.45ms elapsed=11274.33s tid=0x00007f22f40eb490 nid=0xc947 waiting on condition  [0x00007f2242ead000]
   java.lang.Thread.State: WAITING (parking)
        at jdk.internal.misc.Unsafe.park(java.base@17.0.11/Native Method)
        - parking to wait for  <0x000010001bf75c10> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
        at java.util.concurrent.locks.LockSupport.park(java.base@17.0.11/LockSupport.java:211)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@17.0.11/AbstractQueuedSynchronizer.java:715)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@17.0.11/AbstractQueuedSynchronizer.java:938)
        at java.util.concurrent.locks.ReentrantLock$Sync.lock(java.base@17.0.11/ReentrantLock.java:153)
        at java.util.concurrent.locks.ReentrantLock.lock(java.base@17.0.11/ReentrantLock.java:322)
        at com.**Impl.insert(**ApiImpl.java:64) -- 业务代码
        at jdk.internal.reflect.GeneratedMethodAccessor2502.invoke(Unknown Source)
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.11/DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(java.base@17.0.11/Method.java:568)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255)