ringbuffer序列号是全局单调递增的long值,非指针;无head/tail,靠sequence(缓存行填充的atomiclong)驱动;生产者next()获取序号,消费者用独立sequence跟进,代表“已处理到哪”而非“读到哪”。

RingBuffer 的序列号不是“指针”,而是全局单调递增的 long 值
很多人一看到 RingBuffer 就下意识类比 ArrayBlockingQueue 的 head/tail,这是最大误区。Disruptor 里根本没有 head/tail 字段——所有位置都靠一个 Sequence 实例(本质是带缓存行填充的 AtomicLong)驱动。生产者调用 next() 获取下一个可用序号,消费者靠自己的 Sequence 跟进,两者完全解耦。
关键点在于:Sequence 不代表“当前读到哪”,而代表“我已处理到哪”。多个消费者可各自维护独立 Sequence,互不阻塞。这也是实现流水线的基础。
-
bufferSize必须是 2 的幂(如 1024、4096),否则位运算sequence & (bufferSize - 1)失效,性能暴跌 - 序列号本身可溢出(
long溢出后从Long.MIN_VALUE继续),但 Disruptor 内部通过SequenceBarrier的waitFor()检查是否“真正可用”,不依赖数值大小判断逻辑位置 - 不要手动修改
Sequence值——哪怕只是调试打印,也必须用get(),不能用set()或lazySet(),否则破坏屏障语义
SequenceBarrier 是流水线真正的“交通灯”
SequenceBarrier 不是装饰器,它是消费者等待逻辑的核心。它封装了:上游依赖的 Sequence(比如前一个处理器的完成位置)、当前 RingBuffer 的 cursor(最新发布序号)、以及可选的 WaitStrategy(如 BusySpinWaitStrategy)。
当消费者调用 sequenceBarrier.waitFor(nextSequence),实际是在轮询检查“上游是否已就绪 + 当前 buffer 是否有数据”,而非锁住线程。这个过程无锁、无唤醒开销、不触发上下文切换。
- 多阶段流水线中,后一级消费者的
SequenceBarrier必须显式依赖前一级的Sequence,否则会出现数据未处理完就被覆盖(InsufficientCapacityException或静默丢数据) -
TimeoutBlockingWaitStrategy在高吞吐场景下会拖慢整体延迟,千万级必须用BusySpinWaitStrategy或YieldingWaitStrategy - 如果某消费者处理慢,
SequenceBarrier会卡住后续所有依赖它的消费者,形成天然背压——这不是 bug,是设计使然
publish() 必须成对调用:next() → set() → publish()
写入 RingBuffer 不是“放进去就完事”。典型错误是只调用 next() 和 set(),漏掉 publish()。这会导致 SequenceBarrier 永远等不到该序号,下游消费者死锁。
publish() 的作用是将序号“正式提交”,让 cursor 推进,并通知所有等待该位置的 SequenceBarrier。它不操作数据,只更新元信息。
- 批量写入必须用
next(n)+ 循环get()+publish(lo, hi),不能拆成 n 次单个publish(),否则吞吐量断崖下跌 - 若在
set()后、publish()前发生异常,必须调用resetTo()回滚序号,否则 buffer 出现“空洞”,后续消费者可能卡住 -
tryNext()适合突发流量保护,但返回InsufficientCapacityException时别 catch 吞掉——要降级或限流,否则系统雪崩
流水线性能瓶颈往往不在 RingBuffer,而在 EventHandler 的内存分配
千万级下,最常被忽略的是 EventHandler.onEvent() 内部是否 new 对象。即使 RingBuffer 预分配了 event 实例,如果 handler 里又 new String、HashMap 或日志对象,GC 压力立刻回来,延迟毛刺明显。
真实压测中,80% 的 onEvent() 超时都源于堆内临时对象分配,而非 RingBuffer 本身。
- 所有中间状态尽量复用线程局部对象(
ThreadLocal),避免跨事件生命周期持有引用 - 日志输出必须异步且无格式化(如 SLF4J 的
logger.debug("msg:{}", obj)会触发 toString(),改用logger.debug("msg:{}", () -> obj.toString())) - 避免在
onEvent()中调用远程服务或数据库——流水线必须是纯内存计算,IO 要下沉到单独线程池
序列号屏障机制本身很轻,但一旦和业务逻辑耦合,最容易出问题的地方就是 publish 的时机、EventHandler 的 GC 行为、以及多级依赖时 SequenceBarrier 的构造顺序。这些地方错一点,吞吐量就从千万掉到百万,而且现象隐蔽,日志里几乎不报错。











