使用了@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版本不受已知漏洞影响。