iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >从log4j2到Disruptor详解
  • 341
分享到

从log4j2到Disruptor详解

2024-04-02 19:04:59 341人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录log4j2异步日志简要回顾Disruptor在log4j2中的应用异步日志Disruptor启动异步日志Disruptor写入架构及流程Disruptor为什么这么快?Log4

  • log4j2实现原理可查看://www.jb51.net/article/232602.htm
  • 文章同样基于log4j-2.7版本,disruptor-3.3.6

相信看过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

  • 创建RingBuffer与Disruptor绑定
  • RingBuffer根据生产者类型创建对应的实例,例如多生产者:MultiProducerSequencer
  • 创建多生产者序号(bufferSize,waitStrategy)

绑定异常句柄(Disruptor.handleExceptionsWith)

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

  • 根据handle列表创建事件处理器createEventProcessors
  • RingBuffer为Sequence(MultiProducerSequencer)序列创建序列屏障ProcessingSequenceBarrier
  • 创建事件批处理器BatchEventProcessor
  • 为事件批处理器绑定异常处理句柄
  • 消费者仓库(consumerRepository)添加消费者,创建事件处理信息EventProcessorInfo添加至消费者信息列表consumerInfos
  • RingBuffer添加处理序列号列表processorSequences为序列号闸
  • 如果存在序列号屏障,从闸门中移除屏障序列号并标识endOfChain为false

启动Disruptor

  • 遍历消费者仓库放入执行器中执行消费者EventProcessorInfo
  • 启动事件批处理器BatchEventProcessor
  • 事件批处理器序列号自增1
  • 死循环
  • 序列号屏障ProcessingSequenceBarrier等待下个有效序列号,默认为超时等待策略,超时会继续下轮循环
  • 事件批处理器序列号如果小于等于有效序列号
  • 从RingBuffer中按照序列号获取event事件
  • 通知回调事件句柄eventHandler.onEvent如果当前消费下标等于有效序列号availableSequence说明是当前批次的最后一个消息,endOfBatch为true:eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
  • 事件批处理器序列号设置为有效序列号

异步日志Disruptor写入

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

Disruptor获取RingBuffer尝试发布事件tryPublishEvent

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

游标按照步进移动

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

返回有效序列号

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

发布序列号

  • 设置有效序列号至缓存availableBuffer
  • 等待策略唤醒阻塞waitStrategy.signalAllWhenBlocking

架构及流程

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

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

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

  • 环形队列,dataProvide,数据的存储与提供者

Sequencer:生产者

  • 对于所有消费者、生产者(可能是多生产者序列类型对于Multi类型)是同一个实例,包含一个游标序列号Sequence

SequenceBarrier:序列号屏障

  • 对于所有消费者、生产者也是同一个实例,序列号屏障包含一个等待策略、一个RingBuffer引用、一个游标序列号、一个依赖序列号(可能是组序列号类型)

BatchEventProcessor:消费者

  • 消费者包含一个RingBuffer引用
  • 一个序列号屏障,可以包含多个屏障序列号,默认为0个则使用RingBuffer的MultiProducerSequencer的游标序列号Sequence
  • 一个EventHandler:RingBufferLogEventHandler
  • 遍历EventHandler列表将其封装为BatchEventProcessor,将其与原始eventHandler、barrier屏障注册至消费者资源库consumerRepository。
  • 获取batchEventProcessor序列号默认为-1,将其缓存至processorSequences标识正在处理,并将processorSequences、disruptor、consumerRepository绑定至EventHandlerGroup。Disruptor启动遍历消费者资源库启动消费者:BatchEventProcessor

消费者入口

  • 消费者消费前先自增本地序列号(即-1+1=0序号),向序列号屏障申请该序列号的消费,默认为Timeout策略申请。
  • 屏障收到申请waitFor序列号,当前屏障游标序列号小于申请的消费序列号,等待生产者生产至当前序列号,如果超时则抛出异常(本地序列号不更新继续重试);如果没有超时,将屏障的dependentSequence序列号(如果不是非多序列号屏障类型,log4j2使用的是非多序列号屏障,则是屏障的本地游标)赋值为availableSequence返回。
  • 如果availableSequence有效的序列号(即屏障的游标序列号)小于申请要消费的序列号直接返回availableSequence(即消费超出的生产的速度,消费者申请的序列号向后回移至有效序列号)。否则getHighestPublishedSequence判断申请的序列号至availableSequence序列号之间的每个序列号对应的消息事件均是有效的则返回有效序列号(即生产者生产很快,消费者申请消费的序列号很小,向前移动至有效的,可能是本身也可能会跳跃多个下标),根据生产者的availableBuffer判断是否有效,因为生产者先发布序列号再写入数据,此处避免了读取数据异常,如果数据没有写入,有效序列号缓存标识没有写入(即无效),消费者会进行刚刚所说的“重试”,如果之间存在无效序列号则返回申请序列号-1(即回滚一个值,进入逻辑时增加了一个值,也就是回滚至申请前的点,可以理解为与超时相同,即重试)
  • 如果申请的序列号小于等于有效的序列号,则消费序列号对应的消息事件并更新本地BatchEventProcessor的序列号,按照下标去dataProvide(RingBuffer.entries)中提取对应位置的数据消费
  • 如果申请的序列号大于有效的序列号,则将消费者本地序列号设置为有效序列号(即消费超出的生产的速度,消费的序列号向后回移)
  • 如果期间出现任何未catch住的异常则会跳过当前下标,异常出现时的下标及对应的事件会交由exceptionHandler处理,默认为AsyncLoggerDefaultExceptionHandler异步处理,会将异常事件输出至系统的标准错误管道,虽然是异步也是会占用消费者线程池资源

Disruptor:生产者入口

  • 获取RingBuffer尝试发布消息,生产者(例如:MultiProducerSequencer)
  • 生产者游标序列号尝试自增,判断当前是否有足够的空间,当前游标+步进-bufferSize是否大于最小的闸门序列号(gatingSequences,即:所有消费者的本地游标序列号processorSequences列表),最小序列号会缓存至本地gatingSequenceCache用于下次判断减少进行所有闸门序列号的遍历次数,如果是说明已经没有空间(因为生产者生产申请的序列号已经追上了消费者消费序列号的最小值。RingBuffer是一个环形队列结构。上面已经讲到消费者序列号会与生产者序列号同步,同步指消费者申请序列号小于有效序列号时会前进至有效序列号,即使有延迟也保证了有大于等于buffer值的缓冲空间供生产者生产),如果没有空间返回false进入下一轮生产

在这里插入图片描述

  • 自增成功后,将消息转化为对应序列号下标位置的事件数据
  • Sequencer发布序列号,将当前序列号设置为有效(availableBuffer),并根据等待策略唤醒等待的消费者,被唤醒的消费者根据发布的序列号获取相应下标处事件数据进行处理

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];
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: 从log4j2到Disruptor详解

本文链接: https://www.lsjlt.com/news/160550.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 从log4j2到Disruptor详解
    目录log4j2异步日志简要回顾Disruptor在log4j2中的应用异步日志Disruptor启动异步日志Disruptor写入架构及流程Disruptor为什么这么快?Log4...
    99+
    2024-04-02
  • 从log4j2到Disruptor的示例分析
    这篇文章主要为大家展示了“从log4j2到Disruptor的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“从log4j2到Disruptor的示例分析”这篇文章吧。log4j2异步日志...
    99+
    2023-06-22
  • 从实战角度详解Disruptor高性能队列
    目录一、背景二、Java内置队列三、ArrayBlockingQueue的问题1.加锁a.关于锁和CASb.锁c.原子变量2.伪共享a.什么是共享b.缓存行c.什么是伪共享四、Dis...
    99+
    2024-04-02
  • log4j2异步Logger(详解)
    1 异步Logger的意义之前的日志框架基本都实现了AsyncAppender,被证明对性能的提升作用非常明显。在log4j2日志框架中,增加了对Logger的异步实现。那么这一步的解耦,意义何在呢?如图,按我目前的理解:异步Logger是...
    99+
    2023-05-30
    log4j2 异步 logger
  • 详解从ObjectPool到CAS指令
    目录源码解析私有字段构造方法Get 方法Return 方法关于 Interlocked.CompareExchange总结相信最近看过我的文章的朋友对于Microsoft.Exten...
    99+
    2022-11-13
    ObjectPool到CAS指令 CAS指令
  • 如何解决log4j升级log4j2遇到的问题
    这篇文章将为大家详细讲解有关如何解决log4j升级log4j2遇到的问题,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。log4j升级log4j2的问题一、导入包 <!-- log...
    99+
    2023-06-22
  • 详解MySQL从入门到放弃-安装
    学习内容 1.软件安装及服务器设置。 2.(选做,但是强烈建议) 使用图形界面软件 Navicat for SQL 3.数据库基础知识 数据库定义 关系型数据库 二维表 行 列 主...
    99+
    2024-04-02
  • C++封装详解——从原理到实践
    C++封装详解——从原理到实践 引言1.1 什么是封装1.2 为什么使用封装 封装原理2.1. 类和对象2.2 C++类成员的访问权限以及类的封装访问权限控制继承权限公有继承保护继承私有继...
    99+
    2023-10-21
    c++ 开发语言 linux 服务器 qt
  • log4j升级log4j2遇到的问题及解决方式
    目录log4j升级log4j2的问题一、导入包二、在src/main/resources下新建一个log4j2.xml文件升级log4j2遇到的那些坑log4j升级log4j2的问题...
    99+
    2024-04-02
  • JavaScript RegExp 方法详解:从零到精通
    简介: 正则表达式 (regex) 是一种强大的字符串模式匹配工具,能够有效地执行复杂的文本搜索和操作。在 JavaScript 中,RegExp 对象提供了一个接口来使用正则表达式。本文将深入探讨 RegExp 方法,指导您从初学者到...
    99+
    2024-03-09
    JavaScript、正则表达式、RegExp、模式匹配、字符串操作
  • Java 线程池详解:从入门到精通
    Java 线程池是一种用于管理和复用线程的资源池。它提供了创建、销毁和管理线程的统一机制,帮助开发者提升应用程序性能并简化并发编程。 优点 提高性能:线程池可以节省创建和销毁线程的开销,尤其是在需要频繁创建和销毁线程的应用程序中。 控制...
    99+
    2024-03-13
    线程池
  • Go语言切片详解:从基础到高级
    Go语言切片详解:从基础到高级 引言:Go语言是一种快速、可靠的现代编程语言,切片(slice)是其内置的一种数据结构,它是对数组的一个抽象。切片是动态数组,长度可变,与数组相比更加灵...
    99+
    2024-04-02
  • Java 嵌入数据引擎从 SQLite 到 SPL详解
    目录SQLite适应常规基本应用场景SQLite面对复杂场景尚有不足SPL全面支持各种数据源优化体系结构SPL资料可以在Java应用中嵌入的数据引擎看起来比较丰富,但其实并不容易选择...
    99+
    2024-04-02
  • Golang类型转换详解:从入门到精通
    Golang中的类型转换是一种常用的操作,特别是在处理不同数据类型之间的转换时。本文将从基础概念到高级应用,为读者详细解析Golang中类型转换的相关知识,并提供具体的代码示例。 一、...
    99+
    2024-02-26
    golang 类型转换 基础到高级 隐式类型转换
  • Java 详解如何从尾到头打印链表
    目录1.题目2.解法2.1栈2.2递归3.复杂度3.1栈3.2递归1.题目  输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。 题目来源:力扣(Lee...
    99+
    2024-04-02
  • Axios使用方法详解,从入门到进阶
    目录 🌳 Axios的诞生 🌳 Axios的介绍 定义 原理 特性 浏览器支持情况 如何安装  🌳 Axios的使用 ◼️ 创建vue项目 ◼️ Axios的基础用法(get、post、put...
    99+
    2023-10-27
    axios Axios常用写法 Axios Ajax HTTP请求 网络请求
  • C语言从编译到运行过程详解
    目录C语言从编译到运行一、前言二、C程序编译过程三、阶段过程1、预处理阶段2、编译阶段3、汇编阶段4、链接阶段C语言从编译到运行 一、前言 最近在看CSAPP(深入理解计算机系统)然...
    99+
    2024-04-02
  • Python机器学习从ResNet到DenseNet示例详解
    目录从ResNet到DenseNet稠密块体过渡层DenseNet模型训练模型从ResNet到DenseNet 上图中,左边是ResNet,右边是DenseNet,它们在跨层上的...
    99+
    2024-04-02
  • 详解Servlet3.0新特性(从注解配置到websocket编程)
    Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了web开发的效率。主要新特性有以下几个: 引入注解配置 支持web模块化开发 程序异步处理 ...
    99+
    2023-05-31
    servlet3.0 新特性 bs
  • Oracle版本详解:从早期到最新,逐一解析!
    Oracle数据库是全球领先的企业级关系数据库管理系统,自问世以来一直在不断演进,不断升级。本文将从Oracle数据库的早期版本开始,一直到最新版本进行逐一解析,探讨各个版本的特点以及...
    99+
    2024-03-07
    版本 oracle 解析
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作