java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > log4j2到Disruptor

从log4j2到Disruptor详解

作者:会灰翔的灰机

这篇文章主要介绍了从log4j2到Disruptor详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

相信看过log4j2的源码后大家应该明白为什么第二代日志性能会提升那么多,这其中最大的功臣莫过于Disruptor并发编程框架。

下面我们就跟着log4j2来走进Disruptor这个神奇的框(wang)架(zhan)

log4j2异步日志简要回顾

从日志工厂(Log4jLoggerFactory)中获取日志Logger实例

从日志上下文工厂(Log4jContextFactory)获取日志上下文

启用日志上下文(AsyncLoggerContext)

启动Disruptor(AsyncLoggerDisruptor)

序列号屏障(ProcessingSequenceBarrier)等待序列号发布

等待策略(WaitStrategy)等待序列号

返回Logger等待序列号(即等待日志写入)

异步日志(AsyncLogger)写入

日志内容与转化者(RingBufferLogEventTranslator)绑定

Disruptor尝试发布转化者tryPublish

RingBuffer尝试发布事件tryPublishEvent

获取下一个可用序号

转化并发布序号,日志与序号对应的事件绑定,并发布序号

RingBuffer发布序号,MultiProducerSequencer发布

等待策略(waitStrategy)唤醒阻塞

Disruptor在log4j2中的应用

AsyncLoggerDisruptor

异步日志Disruptor启动

创建事件工厂EventFactory

计算ringBufferSize:AsyncLogger.RingBufferSize属性

创建等待策略:AsyncLogger.WaitStrategy属性

创建守护线程执行器executor

创建异步队列满时处理策略AsyncQueueFullPolicy(非Disruptor步骤)

创建Disruptor

绑定异常句柄(Disruptor.handleExceptionsWith)

绑定事件处理句柄(Disruptor.handleEventsWith)

启动Disruptor

异步日志Disruptor写入

尝试发布tryPublish事件转化器EventTranslator:RingBufferLogEventTranslator

Disruptor获取RingBuffer尝试发布事件tryPublishEvent

序列号获取下个有效序号,步进为1,例如:MultiProducerSequencer.tryNext

游标按照步进移动

判断是否有足够的空间,没有则抛出InsufficientCapacityException异常

返回有效序列号

转化器转化消息为对应有效序列号的事件放入entries

发布序列号

架构及流程

红色数字标识流程为获取logger时Disruptor创建消费者流程

黑色数字标识流程为logger写入日志时Disruptor创建事件并通知消费者流程

RingBuffer对于所有消费者、生产者是同一个实例

Sequencer:生产者

SequenceBarrier:序列号屏障

BatchEventProcessor:消费者

消费者入口

Disruptor:生产者入口

在这里插入图片描述

disruptor_new.jpg

Disruptor为什么这么快?

Disruptor采用无锁并发编程,框架中主要使用CAS与volatile关键字保证并发安全

使用环形数据结构(另一个典型的应用是时钟算法也是使用的环形数据结构),为环形结构添加序列号屏障来控制对环形队列读写操作,保证存储数据的并发安全

另一个点便是神奇的缓冲行填充了

Log4j2为什么这么快?

使用Disruptor并发编程框架

使用NIO写入日志数据

当然log4j2中有很多细节,如果我们想要获取线程栈信息,可以同样学习一下这样的写法

// LOG4J2-1029 new Throwable().getStackTrace is faster than Thread.currentThread().getStackTrace().
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
StackTraceElement last = null;
for (int i = stackTrace.length - 1; i > 0; i--) {
    final String className = stackTrace[i].getClassName();
    if (fqcnOfLogger.equals(className)) {
        return last;
    }
    last = stackTrace[i];
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文