nacos 使用一段时间后服务器的系统资源就被耗尽了,这种有处理吗?

使用了@RefreshScope和log4j2,导致每次属性刷新的时候,都会重新加载log4j2,然后多出4个nacos的log4j2异步现场,使用一段时间后服务器的系统资源就被耗尽了,这种有处理吗?每次refresh都会执行这个方法,导致log4j2增加无用的线程+对象fd9508ac10e19e93d512b23d1b6855bf.png

展开
收起
真的很搞笑 2024-03-19 10:40:47 106 分享 版权
阿里云 AI 助理回答

问题分析

根据您的描述,使用了@RefreshScope和Log4j2后,每次属性刷新时都会重新加载Log4j2配置,导致多出4个Nacos相关的Log4j2异步线程。随着时间推移,这些无用的线程会耗尽系统资源。这种情况通常是由于Log4j2的上下文未正确关闭或清理,导致每次刷新时都创建新的线程而旧线程未被回收。

以下是针对该问题的解决方案:


解决方案

1. 手动清理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()确保所有日志记录器都被正确关闭。


2. 优化Log4j2配置

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及以上)。


3. 限制Nacos日志输出

Nacos客户端可能会频繁输出日志,导致Log4j2线程数量增加。您可以通过调整Nacos日志级别来减少不必要的日志输出。

操作步骤: - 在log4j2.xml中为Nacos相关包设置更高的日志级别(如ERROR):

<Logger name="com.alibaba.nacos" level="ERROR" additivity="false">
    <AppenderRef ref="Console"/>
</Logger>

说明: - additivity="false":防止Nacos日志被重复输出到根日志器。


4. 升级Log4j2版本

如果当前使用的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版本时,请确保兼容性,并测试应用是否正常运行。


5. 监控线程池状态

为了进一步排查线程泄漏问题,您可以使用JVM工具(如jstackVisualVM)监控线程池的状态,确认是否有未释放的线程。

操作步骤: - 使用jstack命令导出线程快照:

jstack <pid> > thread_dump.txt
  • 分析thread_dump.txt文件,查找与Log4j2相关的线程(如AsyncAppenderAsyncLogger线程)。

总结与建议

通过上述方法,您可以有效解决因@RefreshScope和Log4j2导致的线程泄漏问题。建议优先尝试手动清理Log4j2上下文优化Log4j2配置,同时升级Log4j2版本以修复潜在问题。如果问题仍未解决,可通过线程监控工具进一步排查。

重要提醒: - 请勿忽略线程泄漏问题,否则可能导致服务器资源耗尽,影响业务稳定性。 - 如果您使用的是阿里云相关服务(如Elasticsearch、HBase等),请确保Log4j2版本不受已知漏洞影响。

有帮助
无帮助
AI 助理回答生成答案可能存在不准确,仅供参考
0 条回答
写回答
取消 提交回答

为企业提供高效、稳定、易扩展的中间件产品。

还有其他疑问?
咨询AI助理