Unlock Ultra‑Low Latency Messaging with LMAX Disruptor: A Step‑by‑Step Java Guide
This article walks through the background, core concepts, and a complete hands‑on demo of LMAX Disruptor, showing how to configure Maven, implement ring buffers, event factories, handlers, producers, and a test suite to achieve high‑throughput, lock‑free messaging in Java.
Background
When a project required a fast in‑memory message queue, Disruptor was selected instead of traditional brokers such as Kafka or RabbitMQ because its primary advantage is raw speed and it is open‑source.
What is Disruptor?
Disruptor was created by the UK foreign‑exchange trading firm LMAX to eliminate the latency of memory queues that, in performance tests, approached the magnitude of I/O latency. A single‑threaded LMAX system can process six million orders per second. The framework gained industry attention after a 2010 QCon presentation. It is a Java library designed to maximize throughput (TPS) and minimize latency for the producer‑consumer problem.
GitHub repository: https://github.com/LMAX-Exchange/disruptor
Core Concepts
Ring Buffer – a circular buffer that stores events. Since version 3.0 its role is limited to storing and updating events; advanced use‑cases can replace it with a custom implementation.
Sequence – a monotonically increasing number that tracks the progress of a specific consumer (or RingBuffer) and prevents false sharing between CPUs.
Sequencer – the core lock‑free algorithm. Two implementations exist: SingleProducerSequencer and MultiProducerSequencer, defining how producers hand events to consumers.
Sequence Barrier – holds references to the main published sequence and dependent consumer sequences, deciding whether a consumer can advance.
Wait Strategy – determines how a consumer waits for the next event (e.g., blocking, yielding, sleeping). Different strategies give different latency‑throughput trade‑offs.
Event – the data object exchanged between producer and consumer; its type is defined by the user.
EventProcessor – owns a consumer’s sequence and runs the event‑loop that invokes the handler.
EventHandler – user‑implemented interface that processes each event; this is the actual consumer logic.
Producer – any code that publishes events to the Disruptor; no special interface is required.
Demo – Step‑by‑Step Implementation
The following eight‑step Maven‑based example builds a complete Disruptor pipeline.
Add Maven dependency
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>Message model
/**
* Message body
*/
@Data
public class MessageModel {
private String message;
}EventFactory
public class HelloEventFactory implements EventFactory<MessageModel> {
@Override
public MessageModel newInstance() {
return new MessageModel();
}
}EventHandler (consumer)
@Slf4j
public class HelloEventHandler implements EventHandler<MessageModel> {
@Override
public void onEvent(MessageModel event, long sequence, boolean endOfBatch) {
try {
// Simulate asynchronous processing
Thread.sleep(1000);
log.info("Consumer start processing");
if (event != null) {
log.info("Consumed message: {}", event);
}
} catch (Exception e) {
log.info("Consumer failed");
}
log.info("Consumer end processing");
}
}Bean manager for Spring context
@Component
public class BeanManager implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.applicationContext = ctx;
}
public static ApplicationContext getApplicationContext() { return applicationContext; }
public static Object getBean(String name) { return applicationContext.getBean(name); }
public static <T> T getBean(Class<T> clazz) { return applicationContext.getBean(clazz); }
}MQManager – creates RingBuffer and Disruptor
@Configuration
public class MQManager {
@Bean("messageModel")
public RingBuffer<MessageModel> messageModelRingBuffer() {
ExecutorService executor = Executors.newFixedThreadPool(2);
HelloEventFactory factory = new HelloEventFactory();
int bufferSize = 1024 * 256; // must be power of two
Disruptor<MessageModel> disruptor = new Disruptor<>(
factory, bufferSize, executor,
ProducerType.SINGLE,
new BlockingWaitStrategy());
disruptor.handleEventsWith(new HelloEventHandler());
disruptor.start();
return disruptor.getRingBuffer();
}
}Service interface and implementation (producer)
public interface DisruptorMqService {
/** Send a message */
void sayHelloMq(String message);
}
@Slf4j
@Component
@Service
public class DisruptorMqServiceImpl implements DisruptorMqService {
@Autowired
private RingBuffer<MessageModel> messageModelRingBuffer;
@Override
public void sayHelloMq(String message) {
log.info("record the message: {}", message);
long sequence = messageModelRingBuffer.next();
try {
MessageModel event = messageModelRingBuffer.get(sequence);
event.setMessage(message);
log.info("Add message to queue: {}", event);
} catch (Exception e) {
log.error("failed to add event", e);
} finally {
// publish must be in finally to avoid blocking later producers
messageModelRingBuffer.publish(sequence);
}
}
}Test class
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class DemoApplicationTests {
@Autowired
private DisruptorMqService disruptorMqService;
@Test
public void sayHelloMqTest() throws Exception {
disruptorMqService.sayHelloMq("Message arrived, Hello world!");
log.info("Message queue sent");
// Verify asynchronous processing
Thread.sleep(2000);
}
}Test Run Output
2020-04-05 14:31:18.543 INFO 7274 --- [main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl : record the message: Message arrived, Hello world!
2020-04-05 14:31:18.545 INFO 7274 --- [main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl : Add message to queue: MessageModel(message=Message arrived, Hello world!)
2020-04-05 14:31:18.545 INFO 7274 --- [main] c.e.utils.demo.DemoApplicationTests : Message queue sent
2020-04-05 14:31:19.547 INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler : Consumer start processing
2020-04-05 14:31:19.547 INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler : Consumed message: MessageModel(message=Message arrived, Hello world!)
2020-04-05 14:31:19.547 INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler : Consumer end processingConclusion
The producer‑consumer pattern is ubiquitous, but Disruptor achieves higher performance because it operates entirely in memory with a lock‑free algorithm, eliminating the overhead of broker‑based queues. Its design—ring buffer, sequence barrier, and configurable wait strategies—allows a single thread to sustain millions of events per second, as demonstrated by the LMAX benchmark of six million orders per second.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
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.
