Disruptor高性能队列 YYDS

一 背景

我们项目中有大量写库操作,当服务出现高并发时会导致数据库IO压力变大。

为了应对这种情况,降低数据库压力,我们需要基于Disruptor设计一套通用的异步批处理流水线。

二 技术选型

其实选择 Disruptor高性能队列之前,本来是想使用 Java内置队列的。

但是都被我pass掉了。原因如下:

concurrentLinkedQueue 虽然是 无锁代表着吞吐量高,但是它内存是不可控的会有OOM风险,以及它的数据结构是链表,频繁的创建和销毁导致GC压力大。

LinkedBlokingQueue 有锁,可选有界 但是数据结构是 链表会有GC压力大。

ArrayBlockingQueue 虽然是 有界队列,内存可控,数据结构为数组对GC友好,但是它存在性能瓶颈原因是因为存在锁竞争,锁竞争就会导致 cpu 工作效率低下,因为时间都花在 锁、挂起、唤醒上。还有一点就是她存在 伪共享,导致缓存命中率下降。

意识到这些问题,决定使用disruptor 高性能队列框架。

原因是因为 disruptor是环形数组天然有界,而且内存是预分配的可控,不会导致OOM,而且采用了CAS来解决锁竞争,使用了缓存行填充解决了 伪共享的问题。

基于 Disruptor 的 无锁设计 和 内存预分配,使得发布事件非常快。Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,每秒可支撑600万订单。

三 实践

核心思想是将高频的单条写入请求,在内存中聚合成批次,最后进行批量写入,极大地减少了数据库的IO次数。

3.1 核心组件

3.1.1 BatchDataRing(RingBuffer 管理器)

负责初始化和管理 Disruptor 框架,提供事件发布接口和注册 ==> 门面模式

主要配置参数
  • ring-size: RingBuffer 大小,默认 4,194,304(1024 * 128 * 32)
  • db-batch-size: 数据库批量处理大小,默认 640
  • db-batch-chunk-size: 数据块大小,默认 40
  • ring-batch-size: RingBuffer 批量处理大小,默认 1024
核心机制
  • 单消费者模式:通过 LimitedThreadFactory 保证只有一个线程处理事件
3.1.2 BatchDataHandler(消费者)

实现 EventHandler 接口,负责处理 RingBuffer 中的事件。

设计亮点是 支持多种数据类型,内部为每种类型维护以下数据结构

数据结构:

private Map<Class,Function<List, Boolean>> funcs = new ConcurrentHashMap<>(); // 类型处理函数映射

private Map<Class,List> caches = new ConcurrentHashMap<>(); // 类型数据缓存映射

3.1.3 数据单元 dataEvent

就是 ringBuffer 流动的盒子

3.1.4 具体的业务适配器

3.2 工作流程

3.2.1 事件发布流程

通过 BatchDataRing.publish() 方法发布事件到 RingBuffer ,事件按 sequence 顺序存储在 RingBuffer 中。

3.2.2 事件处理流程
  • 单线程按 sequence 顺序处理事件
  • 将事件数据按类型分类缓存到对应列表中
  • 达到批量处理条件时触发批量保存
3.2.3 批量处理触发条件
  • 每处理 dbBatchSize(640) 个事件
  • RingBuffer 结束且未达到 ringBatchSize(1024) 的倍数
3.2.4 批量保存机制
  • 遍历所有类型的数据缓存
  • 每种类型数据独立进行批量处理
  • 将每种类型的数据按 dbBatchChunkSize(40) 分割成多个 chunk
  • 使用线程池并发处理所有 chunk
  • 等待所有 chunk 处理完成,记录处理时间

3.3. 并发控制策略

3.3.1 外部串行,内部并发
  • 外部串行:事件按 sequence 顺序由单线程处理,保证处理顺序性
  • 内部并发:批量数据通过线程池并发处理,提升处理性能
3.3.2 线程控制

通过 CAS 操作确保只创建一个处理线程,保证事件处理的顺序性。

java 复制代码
public class LimitedThreadFactory implements ThreadFactory {
    private final AtomicInteger count = new AtomicInteger(0);

    public Thread newThread(Runnable r) {
        if (count.compareAndSet(0, 2)) {
            return new Thread(r);
        } else {
            throw new IllegalStateException("Created more that one thread");
        }
    }
}

3.4. 性能优化设计

3.4.1 分层批量处理
scss 复制代码
RingBuffer (4,194,304) 
    ↓
RingBuffer 批量 (1024)
    ↓
数据库批量 (640)
    ↓
并发处理单元 (40)
3.4.2 多类型数据处理
  • 不同类型数据独立缓存和处理
  • 每种类型数据达到批量条件时独立处理
  • 避免不同类型数据之间的相互阻塞
3.4.3 并发处理优化
  • 使用 Executors.newCachedThreadPool() 线程池
  • 数据分块并发处理,提升 I/O 利用率
相关推荐
魂尾ac11 分钟前
Django + Vue3 前后端分离技术实现自动化测试平台从零到有系列 <第一章> 之 注册登录实现
后端·python·django·vue
CodeSaku1 小时前
是设计模式,我们有救了!!!(七、责任链模式:Chain of Responsibity)
后端
贵州数擎科技有限公司2 小时前
Go-zero 构建 RPC 与 API 服务全流程
后端
笃行3502 小时前
KingbaseES读写分离集群架构解析
后端
IT_陈寒4 小时前
Python 3.12 新特性实战:10个性能优化技巧让你的代码快如闪电⚡
前端·人工智能·后端
绝无仅有5 小时前
前端开发环境搭建:从安装 Node 到成功运行代码
后端·面试·github
yshhuang5 小时前
在Windows上搭建开发环境
前端·后端
绝无仅有5 小时前
某个互联网大厂的Elasticsearch基础面试题与答案
后端·面试·github
无责任此方_修行中5 小时前
AWS IoT Core 成本优化实战:从 PoC 到生产的省钱之旅
后端·架构·aws
ITMan彪叔5 小时前
Java MQTT 主流开发方案对比
java·后端