解决apache高并发日志阻塞的关键是切断日志落盘对请求线程的同步依赖,核心措施为:禁用consoleappender,改用rollingrandomaccessfileappender;启用真正异步(asyncloggercontextselector+disruptor);分级限流、结构化精简日志;调整ringbuffer策略为丢弃而非阻塞。

解决 Apache 在高并发下的日志写入阻塞,关键不是“调大缓冲区”或“换更快磁盘”,而是切断日志落盘对请求处理线程的同步依赖,并控制日志产出节奏与落盘压力。核心思路是:异步化、轻量化、分级化、去瓶颈化。
禁用 ConsoleAppender,改用高性能文件输出
ConsoleAppender 是高并发下最危险的日志出口——System.out 内部存在严重锁竞争,性能比 RollingFileAppender 差 20 倍以上,即使重定向到 /dev/null 也无法规避同步阻塞。必须在生产环境完全禁用。
- 全部切换为
RollingRandomAccessFileAppender:它基于内存映射文件(MappedByteBuffer),绕过 JVM 堆内缓冲和 OutputStream 锁,显著降低 GC 压力和系统调用次数 - 日志路径挂载到本地 SSD,避免 NFS 或网络存储;启用组合滚动策略:
TimeBasedTriggeringPolicy+SizeBasedTriggeringPolicy,防止单文件过大影响追查效率 - 关闭
append="false"(即不强制每次刷盘),允许操作系统缓冲写入,配合immediateFlush="false"
启用真正异步日志,避免主线程卡在 callAppenders
Log4j 1.x 的 Category.callAppenders() 方法内部有 synchronized(c) 锁,高并发时大量线程会 BLOCKED 等待同一 logger 实例——这是典型根源。Log4j2 虽支持异步,但默认仍非真正异步。
Apache Superset 是一个广泛采用的开源 BI 平台,用于 SQL 探索、图表构建和仪表板交付。当代理需要查询仓库数据、组装仪表板或使用成熟的分析界面解释指标而不是临时笔记本代码时,此技能非常有用。
- 必须显式配置:
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector(通过 JVM 参数或 log4j2.xml 中<configuration status="WARN" monitorinterval="30"></configuration>启用) - 全局统一使用
<asynclogger></asynclogger>或开启asyncLogger="true",禁止混用同步 logger - 启动日志中确认出现
Using Disruptor version,表示环形缓冲区已初始化成功
分级限流 + 条件门控 + 结构化格式
大促期间不追求“全量可查”,而要保障“关键可观测”。日志量失控是阻塞的前置诱因。
- 动态降级级别:非核心模块(如缓存操作、HTTP client debug)临时从 DEBUG 切至 WARN;支付、下单等链路保留 INFO 即可
-
加日志门控:不用字符串拼接,改用占位符 + 显式判断,例如:
if (logger.isDebugEnabled()) { logger.debug("order processed, id={}", orderId); } -
结构化精简格式:禁用
%ex、%throwable,改用%xEx(仅异常存在时才格式化);MDC 值提前注入且保持轻量;推荐模式:%d{ISO8601} [%t] %-5p %c{1} - %m%n
调整异步队列策略,防止反压业务线程
异步不是万能的。当 Disruptor RingBuffer 满时,Log4j2 默认采用 DefaultAsyncQueueFullPolicy(阻塞等待),这会直接卡住业务线程。
- 必须将队列满策略改为
DiscardingAsyncQueueFullPolicy:丢弃日志远好于卡住接口 - 增大 RingBuffer 容量:设置
log4j2.asyncLoggerRingBufferSize=262144(默认通常为 2^12 = 4096),适配峰值吞吐 - 避免在日志语句中调用
Throwable.getStackTrace()等耗时操作——这些仍在调用线程执行,仍可能引发局部阻塞










