【零声教育】成也池化,败也池化,池式组件为性能飙升提供技术保障丨大同小异的线程池 丨各不相同的内存池 丨为异步而生的请求池 丨数据库操作的连接池
视频资料代码领取裙:832218493
免费学习地址:C/C++Linux服务器开发/后台架构师
年处理过一个美图的问题,最近又碰到类似问题了,发现跟美图那个案例原因是一样的,在这里拿出来给大家分享一下。
应该是去年6月底,我们私有化发布了新版本,然后就拿去给美图客户安装部署了,美图的美拍应用访问量较大,新版本部署后问题不断,后来我接手去处理,在这之已出过不少问题,客户也不怎么配合了;
问题现象:美图客户的运维说,听云应用kafka积压消息,backend不工作了,重启后不久服务器load值飙高,最高能过万,美图方面不让直接去操作服务器,怀疑我们是否对自己的应用做过压测,让我们自己压测找问题,尴尬啊;最后好不容易拿到了错误日志,开始分析日志问题。
在这里先对load值简单说明一下,它是linux系统或unix系统下cpu的待处理和正在处理的任务的任务队列,有两个原因会导致load飙高, cpu处理不过来或io处理不过来导致等待处理的线程数飙升;
日志里面各种异常一大堆,肯定是某个地方出了问题导致的连锁反应,其中有这样的一些异常:
异常堆栈:
07-07 12:27:11 [pool-9-thread-4] ERROR c.n.n.d.b.p.MobileAppInteractionTraceMessageHandler - failed to write mobileapp interaction trace result to nbfs: unable to create new native thread
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:714)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1368)
at sun.nio.ch.SimpleAsynchronousFileChannelImpl.implWrite(SimpleAsynchronousFileChannelImpl.java:393)
at sun.nio.ch.AsynchronousFileChannelImpl.write(AsynchronousFileChannelImpl.java:251)
at com.networkbench.nbfs.io.NBFSWriter.writeTo(NBFSWriter.java:284)
at com.networkbench.newlens.datacollector.backend.processor.MobileAppInteractionTraceMessageHandler.receive(MobileAppInteractionTraceMessageHandler.java:131)
at com.networkbench.newlens.datacollector.backend.processor.MobileAppInteractionTraceMessageHandler.receive(MobileAppInteractionTraceMessageHandler.java:35)
at com.networkbench.newlens.datacollector.mq.processor.AvroWrappedMessageConsumer$1.run(AvroWrappedMessageConsumer.java:188)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
07-07 12:27:11 [pool-8-thread-2] ERROR c.n.n.d.b.p.MobileAppErrorTraceMessageHandler - failed to write error trace result to nbfs: unable to create new native thread
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:714)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1368)
at sun.nio.ch.SimpleAsynchronousFileChannelImpl.implWrite(SimpleAsynchronousFileChannelImpl.java:393)
at sun.nio.ch.AsynchronousFileChannelImpl.write(AsynchronousFileChannelImpl.java:251)
at com.networkbench.nbfs.io.NBFSWriter.writeTo(NBFSWriter.java:284)
at com.networkbench.newlens.datacollector.backend.processor.MobileAppErrorTraceMessageHandler.receive(MobileAppErrorTraceMessageHandler.java:150)
at com.networkbench.newlens.datacollector.backend.processor.MobileAppErrorTraceMessageHandler.receive(MobileAppErrorTraceMessageHandler.java:38)
at com.networkbench.newlens.datacollector.mq.processor.AvroWrappedMessageConsumer$1.run(AvroWrappedMessageConsumer.java:188)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
分析这个堆栈要用到的知识点:这个异常堆栈涉及到线程池代码,如果你看过线程池的源码,那么分析起来就会比较轻松,否则可能不知道到我在说什么,这里不会去讲线程池源码,请自己找资料去了解,也可以往后翻我的博客,看我写过的一篇源码分析java程序员必精–从源码讲解java线程池ThreadPoolExecuter的实现原理 , 建议一定要弄懂线程池的实现,如果你经常分析线程堆栈就会知道,线程池用到的地方非常多,没有几个应用不使用线程池的;
根据开头的四行线程栈分析可以知线程池在执行addWorker方法时,无法创建线程,抛出了unable to create new native thread的异常,这个异常有点特殊,它并不是指java堆内存溢出了,它说明堆外操作系统的内存已经用尽了,java的线程在java里面只是一个Thread对象,这个Thread对象对应着一个操作系统的线程,每个线程都要分配线程栈,线程栈占用的是堆外操作系统内存,当操作系统内存用尽的时候,再创建线程就会抛出这个异常;