Boost Java Messaging Speed with Disruptor: Complete Tutorial & Demo
This article introduces the high‑performance Disruptor library, explains its core concepts such as Ring Buffer, Sequencer and Wait Strategies, and provides a step‑by‑step Java demo showing how to build a producer‑consumer queue without external brokers.
Background
In a project we needed a fast in‑memory message queue and chose Disruptor, an open‑source Java framework known for its low latency and high throughput.
Disruptor Overview
Disruptor was developed by LMAX Exchange to solve memory‑queue latency issues; a single‑threaded system can handle up to 6 million orders per second. It provides a bounded, lock‑free queue for producer‑consumer scenarios and can be used beyond finance for significant performance gains.
It is an open‑source Java framework designed for maximum TPS and minimal latency.
Implements a bounded queue suitable for producer‑consumer models.
Offers a design pattern that dramatically improves performance for concurrent, buffered, transaction‑processing programs.
Core Concepts
1. Ring Buffer
The ring buffer stores events; since version 3.0 its role is limited to data storage and update, and can be replaced by custom implementations.
2. Sequence
Sequences number events and track consumer progress, avoiding false sharing between CPUs.
3. Sequencer
The Sequencer interface has SingleProducerSequencer and MultiProducerSequencer implementations that manage the fast, correct data transfer between producers and consumers.
4. Sequence Barrier
Maintains references to the main published sequence and dependent consumer sequences, determining whether a consumer can process more events.
5. Wait Strategy
Defines how a consumer waits for the next event; Disruptor provides several strategies with different performance characteristics.
6. Event
An event is the data exchanged between producer and consumer; its type is defined by the user.
7. EventProcessor
Holds a consumer’s sequence and runs the event‑processing loop.
8. EventHandler
The user‑implemented interface that processes events.
9. Producer
Any code that publishes events to the Disruptor; no specific interface is required.
Demo – Step‑by‑Step Implementation
1. Add the Disruptor dependency to
pom.xml:
<code><dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
</code>2. Define the message model:
<code>@Data
public class MessageModel {
private String message;
}
</code>3. Implement an
EventFactorythat creates
MessageModelinstances.
<code>public class HelloEventFactory implements EventFactory<MessageModel> {
@Override
public MessageModel newInstance() {
return new MessageModel();
}
}
</code>4. Create an
EventHandlerthat logs the received message.
<code>@Slf4j
public class HelloEventHandler implements EventHandler<MessageModel> {
@Override
public void onEvent(MessageModel event, long sequence, boolean endOfBatch) {
try {
Thread.sleep(1000);
log.info("Consumer start");
if (event != null) {
log.info("Consumed message: {}", event);
}
} catch (Exception e) {
log.info("Consumer failed");
}
log.info("Consumer end");
}
}
</code>5‑8. Configure the Disruptor bean, a service that publishes messages, and a test that sends a sample string and waits for asynchronous processing.
<code>@Configuration
public class MQManager {
@Bean("messageModel")
public RingBuffer<MessageModel> messageModelRingBuffer() {
ExecutorService executor = Executors.newFixedThreadPool(2);
HelloEventFactory factory = new HelloEventFactory();
int bufferSize = 1024 * 256;
Disruptor<MessageModel> disruptor = new Disruptor<>(factory, bufferSize, executor,
ProducerType.SINGLE, new BlockingWaitStrategy());
disruptor.handleEventsWith(new HelloEventHandler());
disruptor.start();
return disruptor.getRingBuffer();
}
}
</code>The service obtains the next sequence, fills the
MessageModel, publishes it, and the handler processes it asynchronously.
Running the test produces log entries showing the message being recorded, added to the ring buffer, and finally consumed by the handler.
Conclusion
The producer‑consumer pattern is common, but Disruptor implements it entirely in memory with a lock‑free design, which explains its superior performance compared with traditional message brokers.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.