SpringBoot-Log4j2组件引起阻塞hung住问题排查一例

本文主要是介绍SpringBoot-Log4j2组件引起阻塞hung住问题排查一例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、问题现象

 基于SpringBoot的jar包运行的时间会比较长,在运行过程中,进程hung在那里,不再有日志输出,数据库也并没有一直在执行的SQL任务。

二、问题排查

使用Jstack导出java的线程信息如下:

2021-02-22 18:46:38
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.121-b13 mixed mode):"Attach Listener" #99 daemon prio=9 os_prio=0 tid=0x00007f4478001000 nid=0x18f waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"HikariPool-1 housekeeper" #24 daemon prio=5 os_prio=0 tid=0x00007f451e5b8000 nid=0x98 waiting for monitor entry [0x00007f449481b000]java.lang.Thread.State: BLOCKED (on object monitor)at org.apache.logging.log4j.core.appender.OutputStreamManager.writeBytes(OutputStreamManager.java:360)- waiting to lock <0x000000008031aea8> (a org.apache.logging.log4j.core.appender.OutputStreamManager)at org.apache.logging.log4j.core.layout.TextEncoderHelper.writeEncodedText(TextEncoderHelper.java:96)at org.apache.logging.log4j.core.layout.TextEncoderHelper.encodeText(TextEncoderHelper.java:65)at org.apache.logging.log4j.core.layout.StringBuilderEncoder.encode(StringBuilderEncoder.java:68)at org.apache.logging.log4j.core.layout.StringBuilderEncoder.encode(StringBuilderEncoder.java:32)at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:220)at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:58)at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:177)at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:170)at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:161)at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:448)at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:433)at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:417)at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:403)at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:63)at org.apache.logging.log4j.core.Logger.logMessage(Logger.java:146)at org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2163)at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2118)at org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2101)at org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2006)at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1875)at org.apache.logging.slf4j.Log4jLogger.debug(Log4jLogger.java:134)at com.zaxxer.hikari.pool.HikariPool.logPoolState(HikariPool.java:404)at com.zaxxer.hikari.pool.HikariPool$HouseKeeper.run(HikariPool.java:776)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)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)"Abandoned connection cleanup thread" #22 daemon prio=5 os_prio=0 tid=0x00007f451c93e800 nid=0x97 in Object.wait() [0x00007f4495bb1000]java.lang.Thread.State: TIMED_WAITING (on object monitor)at java.lang.Object.wait(Native Method)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)- locked <0x00000000816e8be0> (a java.lang.ref.ReferenceQueue$Lock)at com.mysql.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:64)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)"Service Thread" #17 daemon prio=9 os_prio=0 tid=0x00007f451c2ca800 nid=0x93 runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread11" #16 daemon prio=9 os_prio=0 tid=0x00007f451c2c7800 nid=0x92 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread10" #15 daemon prio=9 os_prio=0 tid=0x00007f451c2c6000 nid=0x91 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread9" #14 daemon prio=9 os_prio=0 tid=0x00007f451c2c4000 nid=0x90 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread8" #13 daemon prio=9 os_prio=0 tid=0x00007f451c2c2000 nid=0x8f waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread7" #12 daemon prio=9 os_prio=0 tid=0x00007f451c2c0000 nid=0x8e waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread6" #11 daemon prio=9 os_prio=0 tid=0x00007f451c2be000 nid=0x8d waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread5" #10 daemon prio=9 os_prio=0 tid=0x00007f451c2bc000 nid=0x8c waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread4" #9 daemon prio=9 os_prio=0 tid=0x00007f451c2ba000 nid=0x8b waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007f451c2b8000 nid=0x8a waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f451c2b6000 nid=0x89 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f451c2b4000 nid=0x88 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f451c2b1000 nid=0x87 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f451c2af800 nid=0x86 runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f451c27d800 nid=0x85 in Object.wait() [0x00007f44973f2000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)- locked <0x00000000802ca438> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f451c278800 nid=0x84 in Object.wait() [0x00007f44974f3000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)at java.lang.Object.wait(Object.java:502)at java.lang.ref.Reference.tryHandlePending(Reference.java:191)- locked <0x00000000802ca668> (a java.lang.ref.Reference$Lock)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)"main" #1 prio=5 os_prio=0 tid=0x00007f451c009800 nid=0x75 runnable [0x00007f4523eb2000]java.lang.Thread.State: RUNNABLEat java.io.FileOutputStream.writeBytes(Native Method)at java.io.FileOutputStream.write(FileOutputStream.java:326)at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)- locked <0x00000000802ba940> (a java.io.BufferedOutputStream)at java.io.PrintStream.write(PrintStream.java:482)- locked <0x00000000802ba920> (a java.io.PrintStream)at org.apache.logging.log4j.core.appender.ConsoleAppender$SystemOutStream.write(ConsoleAppender.java:338)at java.io.PrintStream.write(PrintStream.java:480)- locked <0x000000008031cf78> (a java.io.PrintStream)at org.apache.logging.log4j.core.util.CloseShieldOutputStream.write(CloseShieldOutputStream.java:53)at org.apache.logging.log4j.core.appender.OutputStreamManager.writeToDestination(OutputStreamManager.java:262)- eliminated <0x000000008031aea8> (a org.apache.logging.log4j.core.appender.OutputStreamManager)at org.apache.logging.log4j.core.appender.OutputStreamManager.flushBuffer(OutputStreamManager.java:294)- eliminated <0x000000008031aea8> (a org.apache.logging.log4j.core.appender.OutputStreamManager)at org.apache.logging.log4j.core.appender.OutputStreamManager.flush(OutputStreamManager.java:303)- locked <0x000000008031aea8> (a org.apache.logging.log4j.core.appender.OutputStreamManager)at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:179)at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:170)at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:161)at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:448)at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:433)at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:417)at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:403)at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:63)at org.apache.logging.log4j.core.Logger.logMessage(Logger.java:146)at org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2163)at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2118)at org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2101)at org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2000)at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1859)at org.apache.logging.slf4j.Log4jLogger.debug(Log4jLogger.java:119)at com.ll.hiws.warning.service.impl.WarningResultServiceImpl.InfectCalculateFromResult(WarningResultServiceImpl.java:410)at com.ll.hiws.warning.service.impl.WarningResultServiceImpl.warningCalculate(WarningResultServiceImpl.java:179)at com.ll.hiws.warning.service.impl.WarningResultServiceImpl.InfectCalculate(WarningResultServiceImpl.java:103)at com.ll.hiws.warning.service.impl.WarningResultServiceImpl$$FastClassBySpringCGLIB$$3f8499af.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)at org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$165/1989184704.proceedWithInvocation(Unknown Source)at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)at com.ll.hiws.warning.service.impl.WarningResultServiceImpl$$EnhancerBySpringCGLIB$$57158dfd.InfectCalculate(<generated>)at com.ll.hiws.HiwsApplicationRunner.run(HiwsApplicationRunner.java:70)at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:781)at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:771)at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)at com.ll.hiws.HiwsWarningApplication.main(HiwsWarningApplication.java:11)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)"VM Thread" os_prio=0 tid=0x00007f451c271000 nid=0x83 runnable 

首先可以看到:springboot使用的数据库连接池HikariPool线程处于BLOCKED状态,等待锁 lock <0x000000008031aea8> 释放,

是log4j的输出管理器持有这个锁,

之后的信息也可以看到,日志输出管理器,加了这个锁

main程序持有了锁,数据库连接池等待锁,main程序输出完之后把 锁释放了,数据库连接池就能接着运行了,一切都看起来很正常,而且main程序是RUNNABLE,处于可运行状态,在等待操作系统的资源,没毛病,一切都看起来很正常。

问题就出在,过了会我又导出了一个线程信息,发现与之前导出的信息一模一样,也就是说程序一直没往下执行啊!所以就是main程序的writeBytes一直在等待?!

在jira上找到这样一个回答:ConsoleAppender hangs when writing to System.out in a spawned JVM

有点底层,没整明白,大概意思是 要把ConsoleAppender默认为false的follow设置为true,就解决问题了。

后来我整理了好多资料,这里大概整理下我的理解:

记录日志的时候,如果往控制台打印输出日志的话,会把日志写入缓存,控制台会从缓存中取,但比如控制台没取,比如在IDEA中运行的时候,用鼠标选中控制台,这时候控制台会暂停输出,不从缓存中取东西,缓存内容就不会清,日志程序

会一直往里写,直到写满,线程就会停止写入,等待缓存可用,表现在程序里,就是writeBytes函数不返回,持有的锁不释放,程序就hung住了。

部署在docker中的时候,因为docker容器会一直获取标准输出的内容,自己记录docker日志,但是当缓存中的东西比较多的时候,比如日志长度特别长,docker没办法及时清空缓存,也会导致log4j出现这个问题。但是据我找到的资料,这个问题可以通过升级DOCKER版本得到解决。

等等,往控制台输出的日志会出现这个问题,要是不往控制台输出日志呢?

所以我这里整理下解决方案:

1、在使用 ConsoleAppender 的时候把follow属性设置为true

2、(笔者未验证)改用别的组件,在log4j2框架下,额外引入disruptor,参考:博客园-log4j输出到控制台的性能问题

3、运行时,配置log4j不往控制台输出日志,都写入到文件中,笔者程序正常运行了

4、(笔者未验证)升级docker版本至 18.06 参考:Logging long lines breaks container

 

这里列一下笔者找到的相关连接:

1、ConsoleAppender hangs when writing to System.out in a spawned JVM

2、OutputStreamManager in ConsoleAppender leaking managers

3、Deadlock in log4j 2.10.0

4、Deadlock with logging

5、Logging long lines breaks container

6、A RUNNABLE state thread hang s on the java.io.FileOutputStream.writeBytes method

7、容器打印日志到控制台阻塞的排障

这篇关于SpringBoot-Log4j2组件引起阻塞hung住问题排查一例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/354180

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2