Getting Started with Disruptor

Getting the Disruptor

Get Disruptor

 

 

The Disruptor jar file is available from Maven Central and can be integrated into your dependency manager of choice from there. You can also download the binary from our downloads page on the Wiki. The observant will note that these are just a link to the binaries on Maven Central.

The Disruptor jar files are available from the Maven Central Repository and can be integrated into your optional dependency management, and you can also download the binaries from our download page on the WIKI. The observant will notice that this is just a connection to

Maven repository binaries

 

Basic Event Produce and Consume

Producer and Consumer Basic Events

 

To get started with the Disruptor we are going to consider very simple and contrived example, one that will pass a single long value from a producer to a consumer, where the consumer will simply print out the value. Firstly we will define the Event that will carry the data.

Before we start using the Disruptor we will consider a very simple specially designed example that will pass a Long value between a producer and a consumer, where the consumer just prints out the value. First, we define the events that can move the data

public class LongEvent
{
    private long value;

    public void set(long value)
    {
        this.value = value;
    }
}

 

 

 

In order to allow the Disruptor to preallocate these events for us, we need to an EventFactory that will perform the construction

To allow the Disruptor to pre-assign these events for us, we need an event factory that executes the constructed Event

import com.lmax.disruptor.EventFactory;

public class LongEventFactory implements EventFactory<LongEvent>
{
    public LongEvent newInstance()
    {
        return new LongEvent();
    }
}

 

 

 

Once we have the event defined we need to create a consumer that will handle these events. In our case all we want to do is print the value out the the console.

Once we have identified events, we need to create a consumer that can handle these events. In our case all we want to do is print out the value to the console.

import com.lmax.disruptor.EventHandler;

public class LongEventHandler implements EventHandler<LongEvent>
{
    public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
    {
        System.out.println("Event: " + event);
    }
}

 

We will need a source for these events, for the sake of an example I am going to assume that the data is coming from some sort of I/O device, e.g. network or file in the form of a ByteBuffer.

We need the source of these events, eg I assume the data comes from some kind of I/O device. For example, the Internet or files exist in bytecode form.

import com.lmax.disruptor.RingBuffer;

public class LongEventProducer
{
    private final RingBuffer<LongEvent> ringBuffer;

    public LongEventProducer(RingBuffer<LongEvent> ringBuffer)
    {
        this.ringBuffer = ringBuffer;
    }

    public void onData(ByteBuffer bb)
    {
        long sequence = ringBuffer.next();  // Grab the next sequence
        try
        {
            LongEvent event = ringBuffer.get(sequence); // Get the entry in the Disruptor
                                                        // for the sequence
            event.set(bb.getLong(0));  // Fill with data
        }
        finally
        {
            ringBuffer.publish(sequence);
        }
    }
}

 

 

What becomes immediately obvious is that event publication becomes more invovled that using a simple queue. This is due to the desire for event preallocation. In requires (at the lowest level) a 2-phase approach to message publication, i.e. claim the slot in the ring buffer then publish the available data. It is also necessary to wrap publication in a try/finally block. If we claim a slot in the Ring Buffer (calling RingBuffer.next()) then we must publish this sequence. Failing to do can result in corruption of the state of the Disruptor. Specially, in the multi-producer case this will result in the consumers stalling and being unable to recover without a restart.

What will immediately become apparent? Event publishing becomes something relevant (a simple queue is used). This is because events need to be pre-allocated. Where a 2-stage approach to message publishing is required (at the lowest level), that is,

Declare a slot in the ring buffer and publish the available data. Wrapping the release with a try/finally block is required. If we declare a slot in Ring Buffer (call RingBuffer.next())

, then we have to publish this sequence. Failure can lead to a bad situation for the Disruptor. Especially in the case of multiple producers, this will cause the consumer to stall and not be able to resume unless restarted.

 

Using version 3 Translators

Using Translators version 3.0

With version 3.0 of the Disruptor a richer Lambda-style API was added to help developers by encapsulating this complexity within the Ring Buffer, so post-3.0 the preferred approach for publishing messages is via the Event Publisher/Event Translator portion of the API. E.g.

With version 3.0 of Disruptor, a richer Lambda-style API is provided to help developers by encapsulating this review stuff into Ring Buffer. So a better way to publish information is through the Event Publisher/Event Translator section of the help documentation. for example

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.EventTranslatorOneArg;

public class LongEventProducerWithTranslator
{
    private final RingBuffer<LongEvent> ringBuffer;

    public LongEventProducerWithTranslator(RingBuffer<LongEvent> ringBuffer)
    {
        this.ringBuffer = ringBuffer;
    }

    private static final EventTranslatorOneArg<LongEvent, ByteBuffer> TRANSLATOR =
        new EventTranslatorOneArg<LongEvent, ByteBuffer>()
        {
            public void translateTo(LongEvent event, long sequence, ByteBuffer bb)
            {
                event.set(bb.getLong(0));
            }
        };

    public void onData(ByteBuffer bb)
    {
        ringBuffer.publishEvent(TRANSLATOR, bb);
    }
}

 

The other advantage of this approach is that the translator code can be pulled into a separate class and easily unit tested independently. The Disruptor provides a number of different interfaces (EventTranslator, EventTranslatorOneArg, EventTranslatorTwoArg, etc.) that can be implemented to provide translators. The reason for is to allow for the translators to be represented as static classes or non-capturing lambda (when Java 8 rolls around) as the arguments to the translation method are passed through the call on the Ring Buffer through to the translator.

The advantage of using this approach is that the translator code can be referred to in a separate class, making it easy to use unit tests independently. Disruptor provides many different interfaces (EventTranslator, EventTranslatorOneArg, EventTranslatorTwoArg, etc.) which can be implemented as Translators.

 

The final step is to wire the whole thing together. It is possible to wire all of the components manually, however it can be a little bit complicated so a DSL is provided to simplify construction. Some of the more complicated options are not available via the DSL, however it is suitable for most circumstances.

The last step is to wire the whole thing together. It's possible to wire up all the components manually, it's a bit of a double-check anyway, so the DSL simplifies the construction. Some of the more complex options are not available through the DSL, however, it is suitable for most cases.

import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.RingBuffer;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class LongEventMain
{
    public static void main(String[] args) throws Exception
    {
        // Executor that will be used to construct new threads for consumers
        Executor executor = Executors.newCachedThreadPool();

        // The factory for the event
        LongEventFactory factory = new LongEventFactory();

        // Specify the size of the ring buffer, must be power of 2.
        int bufferSize = 1024;

        // Construct the Disruptor
        Disruptor<LongEvent> disruptor = new Disruptor<>(factory, bufferSize, executor);

        // Connect the handler
        disruptor.handleEventsWith(new LongEventHandler());

        // Start the Disruptor, starts all threads running
        disruptor.start();

        // Get the ring buffer from the Disruptor to be used for publishing.
        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();

        LongEventProducer producer = new LongEventProducer(ringBuffer);

        ByteBuffer bb = ByteBuffer.allocate(8);
        for (long l = 0; true; l++)
        {
            bb.putLong(0, l);
            producer.onData (bb);
            Thread.sleep(1000);
        }
    }
}

 

 

Basic Tuning Options

Basic t-tuning options

 

Using the above approach will work functionally in the widest set of deployment scenarios. However, if you able to make certain assumptions about the hardware and software environment that the Disruptor will run in then you can take advantage of a number of tuning options to improve performance. There are 2 main options for tuning, single vs. multiple producers and alternative wait strategies.

Use the method above to make the function work in the widest deployment scenario. In any case, if you can be sure that the Disruptor is running in both hardware and software environments, there are plenty of tuning options you can take advantage of to improve performance. There are 2 main

Tuning options, single producer versus multiple producers and alternative waiting strategies.

 

Single vs. Multiple Producers

Single producer vs multiple producers

One of the best ways to improve performance in concurrect systems is to ahere to the Single Writer Princple, this applies to the Disruptor. If you are in the situation where there will only ever be a single thread producing events into the Disruptor, then you can take advantage of this to gain additional performance.

One of the best ways to improve performance in concurrent systems is the Single Writer principle, which applies to Disruptors. If you happen to need a single thread to produce events, then you can take advantage of this for additional performance.

public class LongEventMain
{
    public static void main(String[] args) throws Exception
    {
        //.....
        // Construct the Disruptor with a SingleProducerSequencer
        Disruptor<LongEvent> disruptor = new Disruptor(factory,
                                                       bufferSize,
                                                       ProducerType.SINGLE, // Single producer
                                                       new BlockingWaitStrategy(),
                                                       executor);

        //.....
    }
}

 

 

Reference : http://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327072613&siteId=291194637