Disruptor入门教程(译)

Getting the Disruptor

获取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.

Disruptor jar文件可以从Maven中央仓库获取,并且能集成到你的可选的依赖管理,你也能够从我们在WIKI上面的下载页面下载二进制文件。善于观察者将注意到这仅仅是一个连接到

Maven仓库的二进制文件

Basic Event Produce and Consume

生产者和消费者基本事件

扫描二维码关注公众号,回复: 337009 查看本文章

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.

开始使用Disruptor之前 我们将思考专门设计的非常简单的例子,这个例子将在生产者与消费者之间传送Long值,在这里消费者仅仅打印出值。首先,我们定义能够搬运数据的事件

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

为了允许Disruptor预先为我们分配这些事件,我们需要一个执行构造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.

一旦我们有了确定的事件,我们需要创建一个能够处理这些事件消费者。在我们案例中我们想要做的就是在控制台打印出值。

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.

我们需要这些事件的来源,例如我假定数据来源于某种I/O设备。比如,互联网或者以字节码形式存在文件。

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.

什么东西将立刻变得明显?事件发布变得相关东西(使用了一个简单队列)。这是由于事件需要预先分配。在需要(在最低的水平)2段式方法消息发布,也就是,

在ring buffer里面声明获取一个槽,然后发布可用的数据。使用try/finally块包裹发布是必须的。如果我们在Ring Buffer(调用RingBuffer.next())里声明获取了一个槽

,那么我们必须发布这个sequence。失败能够导致Disruptor情况糟糕。特别是在多生产者的情况下,这将导致消费者停顿并且不能够恢复,除非重启。

Using version 3 Translators

使用3.0版本的Translators

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.

使用3.0版本的Disruptor,通过把这种复查的东西封装到Ring Buffer里面, 一个更加丰富的Lambda风格的API被提供帮助开发人员。因此 发布信息的更佳的方式是通过帮助文档的Event Publisher/Event Translator部分。比如

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.

使用这种方式这个优势是translator代码能够被提到一个单独的类中,容易独立地使用单元测试。Disruptor提供很多不同接口(EventTranslator, EventTranslatorOneArg, EventTranslatorTwoArg,等等),这些接口能过被实现成Translator.

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.

最后一步是把整个事情连接在一起.可以手动连接所有组件,无论如何它有一点复查,因此DSL简化了构造。一些更加复杂的选项通过DSL是无法使用,无论如何,它适合于大部分情况。

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

基本的t调优选项

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.

使用上面的方法将功能工作在最宽的部署场景。无论如何,如果你能确定Disruptor运行在硬件和软件的环境,那么你可以利用大量的调优选项来提高性能。有2个主要的

调优选项,单个生产者与多个生产者对比和可供选择的等待策略。

Single 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.

在并发系统中改进性能最好的方法之一是Single Writer原则,这适用于Disruptor.如果你正好需要单个线程来生产事件,那么你可利用这个获取附加性能。

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);

        //.....
    }
}

参考:https://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started

猜你喜欢

转载自weigang-gao.iteye.com/blog/2323675