Spring Boot Log4j2线程被阻塞

问题描述

在我们的Prod环境中,应用突然卡住,没有任何请求得到处理,所以,我进行了PID的线程转储

我看到Log4j被其他线程锁定的对象被阻止

共享threaddump的各个部分

    ```"http-nio-8080-exec-21025" #4910868 daemon prio=5 os_prio=0 tid=0x00007f2b3016a000 nid=0x2d87 runnable [0x00007f2b06d9e000]
   java.lang.Thread.State: RUNNABLE
    at java.io.FileOutputStream.writeBytes(Native Method)
    at java.io.FileOutputStream.write(FileOutputStream.java:326)
    at org.apache.logging.log4j.core.appender.OutputStreamManager.writetoDestination(OutputStreamManager.java:250)
    - eliminated <0x00000000deb28ba8> (a org.apache.logging.log4j.core.appender.rolling.RollingFileManager)
    at org.apache.logging.log4j.core.appender.FileManager.writetoDestination(FileManager.java:273)
    - eliminated <0x00000000deb28ba8> (a org.apache.logging.log4j.core.appender.rolling.RollingFileManager)
    at org.apache.logging.log4j.core.appender.rolling.RollingFileManager.writetoDestination(RollingFileManager.java:240)
    - eliminated <0x00000000deb28ba8> (a org.apache.logging.log4j.core.appender.rolling.RollingFileManager)
    at org.apache.logging.log4j.core.appender.OutputStreamManager.flushBuffer(OutputStreamManager.java:282)
    - eliminated <0x00000000deb28ba8> (a org.apache.logging.log4j.core.appender.rolling.RollingFileManager)
    at org.apache.logging.log4j.core.appender.OutputStreamManager.flush(OutputStreamManager.java:291)
    - **locked <0x00000000deb28ba8>** (a org.apache.logging.log4j.core.appender.rolling.RollingFileManager)```

    ```"http-nio-8080-exec-20991" #4899507 daemon prio=5 os_prio=0 tid=0x00007f2b30163000 nid=0x7e98 waiting for monitor entry [0x00007f2a62dcd000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at org.apache.logging.log4j.core.appender.rolling.RollingFileManager.checkRollover(RollingFileManager.java:272)
    - **waiting to lock <0x00000000deb28ba8>** (a org.apache.logging.log4j.core.appender.rolling.RollingFileManager)
    at org.apache.logging.log4j.core.appender.RollingFileAppender.append(RollingFileAppender.java:311)
    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) ```
    <Console name="Console-Appender" target="SYstem_OUT">
          <PatternLayout>
            <pattern>
                 [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
            </pattern>
        </PatternLayout>  
    </Console>
    <RollingFile name="RollingFile-Appender" fileName="@log4j2.path@/rbac.log" filePattern="@log4j2.path@/rbac.log.%d{yyyy-MM-dd}">
        <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [${bundle:application:spring.application.name},%X{X-B3-TraceId},%X{X-B3-SpanId}] %c{1} - %msg%n"/>
        <Policies>
             <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        </Policies>
        <DefaultRolloverStrategy max="100"/>
  </RollingFile>        
</Appenders>
  
<Loggers>
  <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Console-Appender"/>
        </Logger>
      <Logger name="com.pisp" level="info" additivity="false">
          <AppenderRef ref="RollingFile-Appender" level="info"/>        
      </Logger>
  <Root level="info">
          <AppenderRef ref="Console-Appender" />
          <AppenderRef ref="RollingFile-Appender" />
      </Root>
</Loggers>```


> Can Someone Help me fix this issue with Spring Boot Log4j2

Java -8 
Spring Boot Version - 2.3.2.RELEASE
spring-boot-starter-log4j2

解决方法

由于未处理新请求并且应用程序冻结,这可能表明您的应用程序遇到了死锁。可能发生死锁的最简单情况是,当您有两个线程( ThreadA ThreadB )和两个资源( R1 R2 ),一次只能由一个线程使用。这样的资源可以是

  • 数据库的悲观锁,防止数据库行被修改
  • 监视对象保护synchronized方法或由java.util.councurrency类,CountDownLatchSemaphoreBarrier类的对象使用

让我们假设两个线程(ThreadA,ThreadB)都需要获得两个资源(R1和R2)才能执行某些操作。如果两个线程尝试同时执行该操作,但顺序不同,则可能会发生死锁:

  1. 资源R1和R2是免费的
  2. 线程A获得资源R1-即输入同步方法
  3. ThreadB获得资源R2-即获得获得的悲观锁
  4. 线程A尝试获取资源R2,但是线程B占用了它,因此它等待 无限期,直到可以继续进行操作流程为止
  5. ThreadB尝试获取资源R1,但是它被ThreadA占用,因此它会无限期地等待,直到有空继续进行操作流程为止
  6. 从现在开始,需要获取这些资源之一的所有其他线程也开始无限期等待,并且应用程序被卡住

线程转储可以帮助您找到等待线程的监视器对象。对于数据库悲观锁,每个数据库通常都有一些工具,可以列出获得的悲观锁和与之相关的查询。

由于涉及到线程在RollingFileManager中等待,所以我建议检查类中toString()方法的实现是否不直接或间接调用某些同步方法。另外,请确保您的应用程序有足够的存储空间来存储日志并有权写入日志-也许此问题的根本原因是比较平淡的。