使用了@RefreshScope和log4j2,导致每次属性刷新的时候,都会重新加载log4j2,然后多出4个nacos的log4j2异步现场,使用一段时间后服务器的系统资源就被耗尽了,这种有处理吗?每次refresh都会执行这个方法,导致log4j2增加无用的线程+对象
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
根据您的描述,使用了@RefreshScope和Log4j2后,每次属性刷新时都会重新加载Log4j2配置,导致多出4个Nacos相关的Log4j2异步线程。随着时间推移,这些无用的线程会耗尽系统资源。这种情况通常是由于Log4j2的上下文未正确关闭或清理,导致每次刷新时都创建新的线程而旧线程未被回收。
以下是针对该问题的解决方案:
在Spring Cloud中,@RefreshScope触发属性刷新时,默认不会自动清理Log4j2的上下文。您需要在属性刷新时手动清理Log4j2的上下文,以避免重复加载和线程泄漏。
操作步骤: - 在属性刷新的监听器中,调用Log4j2的LoggerContext清理方法。 - 示例代码如下:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class Log4j2RefreshHandler {
    @EventListener(RefreshScopeRefreshedEvent.class)
    public void handleRefreshScopeRefreshedEvent() {
        // 获取Log4j2的LoggerContext
        LoggerContext context = (LoggerContext) LogManager.getContext(false);
        if (context != null) {
            // 停止并清理Log4j2上下文
            context.stop();
            LogManager.shutdown();
        }
    }
}
说明: - LoggerContext.stop()会停止当前Log4j2上下文的所有线程和资源。 - LogManager.shutdown()确保所有日志记录器都被正确关闭。
Log4j2的异步日志功能可能会导致线程池未正确释放。您可以通过以下方式优化Log4j2配置,减少线程泄漏的风险。
操作步骤: - 确保Log4j2的异步日志使用AsyncLogger而非AsyncAppender,因为AsyncLogger性能更高且线程管理更优。 - 在log4j2.xml中添加以下配置:
<Configuration status="WARN" shutdownHook="disable">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss:SSS zzz} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
关键点: - shutdownHook="disable":禁用Log4j2的默认关闭钩子,避免与Spring的生命周期冲突。 - 使用AsyncLogger时,需引入disruptor依赖(版本建议为3.4.4及以上)。
Nacos客户端可能会频繁输出日志,导致Log4j2线程数量增加。您可以通过调整Nacos日志级别来减少不必要的日志输出。
操作步骤: - 在log4j2.xml中为Nacos相关包设置更高的日志级别(如ERROR):
<Logger name="com.alibaba.nacos" level="ERROR" additivity="false">
    <AppenderRef ref="Console"/>
</Logger>
说明: - additivity="false":防止Nacos日志被重复输出到根日志器。
如果当前使用的Log4j2版本较低,可能存在已知的线程管理问题。建议升级到最新稳定版本(如2.20.0及以上),以修复潜在的线程泄漏问题。
操作步骤: - 在pom.xml中更新Log4j2依赖版本:
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.20.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.20.0</version>
</dependency>
注意: - 升级Log4j2版本时,请确保兼容性,并测试应用是否正常运行。
为了进一步排查线程泄漏问题,您可以使用JVM工具(如jstack或VisualVM)监控线程池的状态,确认是否有未释放的线程。
操作步骤: - 使用jstack命令导出线程快照:
jstack <pid> > thread_dump.txt
thread_dump.txt文件,查找与Log4j2相关的线程(如AsyncAppender或AsyncLogger线程)。通过上述方法,您可以有效解决因@RefreshScope和Log4j2导致的线程泄漏问题。建议优先尝试手动清理Log4j2上下文和优化Log4j2配置,同时升级Log4j2版本以修复潜在问题。如果问题仍未解决,可通过线程监控工具进一步排查。
重要提醒: - 请勿忽略线程泄漏问题,否则可能导致服务器资源耗尽,影响业务稳定性。 - 如果您使用的是阿里云相关服务(如Elasticsearch、HBase等),请确保Log4j2版本不受已知漏洞影响。