高并发下log4j的性能瓶颈
开篇
近期由于业务需要进行业务迁移,期间因为误设置log4j的日志级别,导致系统性能整体下降,具体表现在QPS下降明显,系统RT上升。迁移期间由于各类系统环境较原来有较大差别,因为在排查过程中也走了一些弯路,总结起来避免他人再次踩坑。
问题背景
问题的背景其实很简单,线上系统的log4j的日志级别由warn调整为info,导致大量的调用log4j的日志接口,导致系统的rt上升伴随着qps下降,具体的影响效果可以看下图。
需要额外解释的就是log4j的这个问题需要在qps较大的情况下才会复现,按照我遇到的情况基本上需要在2k/s以上会比较明显(数据不一定非常准确),只是为了说明需要在大量请求的情况才会触发synchronized的问题。
当然问题的本质在于大量调用log4j的日志接口,导致竞争synchronized影响性能,影响的时间应该也是在ms级别,这其实在另外一个方面说明了只求非常极致追求性能问题才会细致的去分析这些问题。
优化效果
优化后qps的提升效果图,这边是以分钟进行统计的。
优化后系统的响应时间效果图。
现象分析
整个分析过程其实并不是那么具有条理性,只是现在总结起来看上去像那么回事。
- 对比两套系统的差异点,发现系统的log4j的日志级别不一致,尝试修改日志级别观察效果。
- 发现日志级别调高后系统性能明显提升,开始怀疑log4j的问题并网上查阅资料。
- jstack打印对应的日志,发现在不同日志级别情况下下面的BLOCKED的日志数量不一样。
- 发现竞争锁的日志waiting to lock <0x00007f3a04020830>。
- 观察jstack日志并找到log4j的源码发现代码当中有同步锁关键字synchronized。
#514 daemon prio=5 os_prio=0 tid=0x00007f369002f800 nid=0x561a waiting for monitor entry [0x00007f348fb96000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.log4j.Category.callAppenders(Category.java:204)
- waiting to lock <0x00007f3a04020830> (a org.apache.log4j.spi.RootLogger)
at org.apache.log4j.Category.forcedLog(Category.java:391)
at org.apache.log4j.Category.log(Category.java:856)
#521 daemon prio=5 os_prio=0 tid=0x00007f367c042800 nid=0x5617 waiting for monitor entry [0x00007f348fe99000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.log4j.Category.callAppenders(Category.java:204)
- waiting to lock <0x00007f3a04020830> (a org.apache.log4j.spi.RootLogger)
at org.apache.log4j.Category.forcedLog(Category.java:391)
at org.apache.log4j.Category.log(Category.java:856)
原因分析
直接定位log4j的源码位置,发现synchronized的关键字,基本上这时候其实是验证之前的猜想而已。
关于synchronized的性能问题,可以网上去参考java几种锁的性能对比,只记得一个简单的结论synchronized在大量竞争的情况下劣势比较明显各种同步方法性能比较(synchronized,ReentrantLock,Atomic)。
void callAppenders(LoggingEvent event) {
int writes = 0;
for(Category c = this; c != null; c=c.parent) {
// Protected against simultaneous call to addAppender, removeAppender,...
synchronized(c) {
if(c.aai != null) {
writes += c.aai.appendLoopOnAppenders(event);
}
if(!c.additive) {
break;
}
}
}
if(writes == 0) {
repository.emitNoAppenderWarning(this);
}
}
心得
老大曾说过,凡事做个有心人,会发现更多的东西。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
spring 注解试事物源码解析
spring 注解试事物源码解析 基于xml注解式事务入口 public class TxNamespaceHandler extends NamespaceHandlerSupport { static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager"; static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager"; static String getTransactionManagerName(Element element) { return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ? element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME); } @Override public void init() {...
-
下一篇
HTML5语法
HTML编码惯例 Web开发人员在使用HTML时,往往有不确定的编码风格和语法. 2000和2010之间,许多Web开发人员从HTML转换到XHTML. 在XHTML中,开发人员被迫写出有效的和结构良好的代码. HTML5说到的代码验证有点儿马虎. 聪明和未来的证明 一个一贯使用的风格,让别人了解你的HTML更容易. 在未来,如XML阅读器,可以阅读你的HTML. 使用形式好的更“接近XHTML的”语法,可以更聪明. 始终保持您的代码整洁,干净,并形成良好. 使用正确的文档类型 始终将文档类型声明为文档中的第一行: <!DOCTYPE html> 如果你想私有小写标签,您可以使用: <!doctype html> 使用小写的元素名称 HTML5允许混合大写和小写字母的元素名称. 我们建议使用小写元素名称,因为: 混合大写和小写的名字是不好的 开发者通常使用小写名称 (像在XHTML里面一样) 小写字母看起来更干净 小写更容易写 不好的: <SECTION> <p>这是一个段落.</p> </SECTION> 非常不...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- MySQL数据库在高并发下的优化方案
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8编译安装MySQL8.0.19