Getting started with disruptor

Source: Internet
Author: User

In a recent project, I saw my colleagues use disruptor. I used to see disruptor articles on ifeve, but I didn't have any in-depth research. Now I have used it in the project, take this opportunity to learn more about this concurrent programming framework. The project uses disruptor-2.10.4, so the code of the disruptor analyzed below is of this version.

The article about disruptor in the concurrent programming network is disruptor1.0. Therefore, some terms are no longer available or replaced in version 2.0.

Disruptor terminology

On GitHub, the disruptor wiki explains the terms in disruptor. When I look at disruptor, I think it is necessary to put them together with several other classes.

  • RingBufferIt is often seen as the main component of disruptor. However, from 3.0 onwards, ringbuffer is only responsible for storing and updating data circulating in disruptor. In some special scenarios, it can be completely replaced by users (using other data structures.
  • SequenceDisruptor uses sequence to represent the serial number processed by a special component. Like disruptor, each consumer (eventprocessor) maintains a sequence. Most of the concurrent Code depends on the running of these sequence values, so sequence supports multiple features of the current atomiclong class. In fact, the only difference between the two is that sequence contains additional features to prevent sequence from sharing with other values.
  • SequencerThis is the true core of disruptor. The two producers (single producers and multiple producers) that implement this interface implement all the concurrent algorithms to implement accurate and fast data transmission between producers and consumers.
  • SequenceBarrierGenerated by sequencer and contains the reference of published sequence. These sequence come from sequencer and sequence of some independent consumers. It contains the logic to determine whether there is an event for consumers to consume.
  • Waitstrategy: it determines how a consumer will wait for the producer to put the event into disruptor.
  • EventData units processed from the producer to the consumer. No code in disruptor indicates event because it is completely user-defined.
  • EventProcessorThe main event loop is used to process events in disruptor and has the sequence of the consumer. One implementation class is batcheventprocessor, which includes the effective implementation of event loop and calls back the implementation object of an eventhandler interface.
  • EventHandlerImplemented by the user and represents a consumer interface in the disruptor.
  • ProducerImplemented by the user. It calls ringbuffer to insert an event, and there is no corresponding implementation code in disruptor, which is implemented by the user.
  • WorkProcessorMake sure that each sequence is consumed by only one processor. processing multiple workprocessors in the same workpool does not consume the same sequence.
  • WorkerPoolA workprocessor pool where workprocessor consumes sequence. Therefore, a task can be handed over between workers that implement the workhandler interface.
  • LifecycleAwareWhen batcheventprocessor is started and stopped, this interface is implemented to receive notifications.
Disruptor impressions

At first glance, disruptor gave the impression that ringbuffer is its core. The producer writes elements to ringbuffer, and the consumer consumes elements from ringbuffer, such:

This is the simplest disruptor model. The ringbuffer is organized into a ring queue, but it is different from the queue we often use. The queue size is fixed, and each element slot is numbered with an integer, only one cursor in ringbuffer maintains a sequence number pointing to the next available position. Each time the producer writes an element to ringbuffer, it must apply for a writable sequence number to ringbuffer, if there are available nodes in ringbuffer at this time, ringbuffer returns the sequence number of this available node to the producer. If not, wait. The element sequence number consumed by the consumer must also be the element sequence number that the producer has written.

So how does disruptor implement these logics? Let's take a look at a disruptor example.

Disruptor example

It is not applicable to disruptor DSL and is directly completed using classes in disruptor.

// The public class intevent {private int value =-1; Public int getvalue () {return value;} public void setvalue (INT value) {This. value = value;} Public String tostring () {return string. valueof (value);} public static eventfactory <intevent> int_enevt_factory = new eventfactory <intevent> () {public intevent newinstance () {return New intevent ();}};} // producer public class inteventproducer implements workhandler <intevent> {private int seq = 0; Public void onevent (intevent event) throws exception {system. out. println ("produced" + SEQ); event. setvalue (++ SEQ) ;}}// consumer public class inteventprocessor implements workhandler <intevent> {public void onevent (intevent event) throws exception {system. out. println (event. getvalue (); event. setvalue (1) ;}} public class disruptortest {public static void main (string [] ARGs) throws interruptedexception {// create a ringbuffer object ringbuffer <intevent> ringbuffer = new ringbuffer <intevent> (intevent. int_enevt_factory, new singlethreadedclaimstrategy (16), new sleepingwaitstrategy (); sequencebarrier = ringbuffer. newbarrier (); inteventproducer [] Producers = new inteventproducer [1]; for (INT I = 0; I <producers. length; I ++) {producers [I] = new inteventproducer ();} workerpool <intevent> crawler = new workerpool <intevent> (ringbuffer, sequencebarrier, new inteventexceptionhandler (), producers); sequencebarrier sb = ringbuffer. newbarrier (crawler. getworkersequences (); inteventprocessor [] processors = new inteventprocessor [1]; for (INT I = 0; I <processors. length; I ++) {processors [I] = new inteventprocessor ();} workerpool <intevent> applier = new workerpool <intevent> (ringbuffer, Sb, new inteventexceptionhandler (), processors); List <sequence> gatingsequences = new arraylist <sequence> (); For (sequence S: crawler. getworkersequences () {gatingsequences. add (s) ;}for (sequence S: applier. getworkersequences () {gatingsequences. add (s);} ringbuffer. setgatingsequences (gatingsequences. toarray (new sequence [gatingsequences. size ()]); threadpoolexecutor executor = new threadpoolexecutor (7,7, 10, timeunit. minutes, new linkedblockingqueue <runnable> (5); crawler. start (Executor); applier. start (Executor); While (true) {thread. sleep (1000); long lastseq = ringbuffer. next (); ringbuffer. publish (lastseq) ;}} class inteventexceptionhandler implements exceptionhandler {public void handleeventexception (throwable ex, long sequence, object event) {} public void handleonstartexception (throwable ex) {} public void handleonshutdownexception (throwable ex ){}}


In the above Code, the intevent class is the event in terms of terms. The inteventproducer corresponds to the producer, and the inteventprocessor corresponds to the eventprocessor, that is, the consumer. However, the inteventprocessor class is not the implemented inteventprocessor interface.

The main method is used for analysis.

The first is the creation of ringbuffer,RingBuffer<IntEvent> ringBuffer = new RingBuffer<IntEvent>(IntEvent.INT_ENEVT_FACTORY,new SingleThreadedClaimStrategy(16), new SleepingWaitStrategy());The first parameter of the ringbuffer constructor is the class that implements the eventfactory interface. The main function is to create an intevent object. When creating a ringbuffer object, the second parameter isClaimStrategy, The producer passesClaimStrategyTo apply for the next available node. The third parameter isWaitStrategyImplementation class, which defines the consumer's wait policy.

The following code initializes the producer.

SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();IntEventProducer[] producers = new IntEventProducer[1];for (int i = 0; i < producers.length; i++) {    producers[i] = new IntEventProducer();}WorkerPool<IntEvent> crawler = new WorkerPool<IntEvent>(ringBuffer,sequenceBarrier,new IntEventExceptionHandler(), producers);

ringBuffer.newBarrier()The returned result is a sequencebarrier object. barrier, as its name implies, is an obstacle that prevents the producer from crossing the position in the ringbuffer it can reach. Ringbuffer maintains a cursor during execution. Therefore, the cursor obtained by the producer from ringbuffer must be smaller than or equal to this cursor.

Then there is the consumer initialization:

SequenceBarrier sb = ringBuffer.newBarrier(crawler.getWorkerSequences());IntEventProcessor[] processors = new IntEventProcessor[1];for (int i = 0; i < processors.length; i++) {    processors[i] = new IntEventProcessor();}WorkerPool<IntEvent> applier = new WorkerPool<IntEvent>(ringBuffer,sb,new IntEventExceptionHandler(),processors);

The consumer also needs to set a sequencebarrier object for initialization. This sequencebarrier object specifies the sequence number of elements that the consumer can consume. If the consumer's cursor is greater than the sequence number, the consumer mustWaitStrategyDefines the policy wait.

After the producer and consumer are created, the next step is to setRingBufferA variablegatingSequences,gatingSequencesThe role of ringbuffer is to prevent the producer from overwriting elements that have not been consumed by the consumer. If the size of a ringbuffer is 8 and the consumption speed of the consumer is slow, the ringbuffer may be full, when the producer applies for the next available serial number from ringbuffer, the serial number that has not been consumed by the consumer cannot be overwritten. Therefore, ringbuffer cannot return the available serial number to the producer, and the consumer thread enters the waiting state, in this case, ringbuffer checks whether the serial number of the current application is greatergatingSequencesIf the currently applied sequence number is greater than the minimum sequence number, the producer will wait. The Code is as follows:

List<Sequence> gatingSequences = new ArrayList<Sequence>();for(Sequence s : crawler.getWorkerSequences()) {    gatingSequences.add(s);}for(Sequence s : applier.getWorkerSequences()) {    gatingSequences.add(s);}     ringBuffer.setGatingSequences(gatingSequences.toArray(new Sequence[gatingSequences.size()]));


The sequence number of the producer is added because when multiple producers exist, one producer cannot overwrite the sequence number applied by another producer.

The following code starts the disruptor thread to execute the producer and consumer threads.

ThreadPoolExecutor executor = new ThreadPoolExecutor(7, 7,10,TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(2));crawler.start(executor); applier.start(executor);

The last thing to do is to continuously apply for an available serial number by the producer, and submit the prepared element serial number, that is:

while (true) {    Thread.sleep(1000);    long lastSeq = ringBuffer.next();    ringBuffer.publish(lastSeq);}


This process is generally two-phase commit, the first-phase callRingBuffer.next()Method to obtain the next available node. At this time, the onevent method of the producer class is called, that isIntEventProducer.onEvent()The second stage is to release the number of elements that have been produced.lastSeqTo remind the consumer that the serial number can be consumed.
Summary

This article briefly introduces some terms of disruptor, explains the main points of the execution process of a disruptor sample program, and understands the execution process of disruptor from a macro perspective, in the next article, we will analyze the internal execution principle of disruptor.

Reference

Concurrent framework disruptor Translation
Java concurrent programming Study Notes: unsafe class
From Java code to Java heap
GitHub of disruptor
Disruptor User Guide

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.