并发编程Disruptor入门

来源:互联网 发布:入侵服务器修改数据 编辑:程序博客网 时间:2024/05/16 01:00

获得Disruptor
可以通过Maven或者下载jar来安装Disruptor。只要把对应的jar放在Java classpath就可以了。

基本的事件生产和消费
我们从一个简单的例子开始学习Disruptor:生产者传递一个long类型的值给消费者,而消费者消费这个数据的方式仅仅是把它打印出来。首先声明一个Event来包含需要传递的数据

public class LongEvent {     private long value;    public long getValue() {         return value;     }     public void setValue(long value) {         this.value = value;     } }

由于需要让Disruptor为我们创建事件,我们同时还声明了一个EventFactory来实例化Event对象

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

我们还需要一个事件消费者,也就是一个事件处理器。这个事件处理器简单地把事件中存储的数据打印到终端

public class LongEventHandler implements EventHandler<LongEvent>{    public void onEvent(LongEvent longEvent, long l, boolean b)            throws Exception {        System.err.println("收到数据了:"+longEvent.getValue());    }}

事件都会有一个生成事件的源,这个例子中假设事件是由于磁盘IO或者network读取数据的时候触发的,事件源使用一个ByteBuffer来模拟它接受到的数据,也就是说,事件源会在IO读取到一部分数据的时候触发事件(触发事件不是自动的,程序员需要在读取到数据的时候自己触发事件并发布)

public class LongEventProducer {    private final RingBuffer<LongEvent> ringBuffer;    public LongEventProducer(RingBuffer<LongEvent> ringBuffer) {        this.ringBuffer = ringBuffer;    }    /**     * onData用来发布事件,每调用一次就发布一次事件事件 它的参数会通过事件传递给消费者     *      * @param b     */    public void onData(ByteBuffer b) {        // 可以把ringBuffer看做一个事件队列,那么next就是得到下面一个事件槽        long sequence = ringBuffer.next();        try {            // 用上面的索引取出一个空的事件用于填充            LongEvent event = ringBuffer.get(sequence);            // for the sequence            event.setValue(b.getLong(0));        } finally {            // 发布事件            ringBuffer.publish(sequence);        }    }}

很明显的是:当用一个简单队列来发布事件的时候会牵涉更多的细节,这是因为事件对象还需要预先创建。发布事件最少需要两步:获取下一个事件槽并发布事件(发布事件的时候要使用try/finnally保证事件一定会被发布)。如果我们使用RingBuffer.next()获取一个事件槽,那么一定要发布对应的事件。如果不能发布事件,那么就会引起Disruptor状态的混乱。尤其是在多个事件生产者的情况下会导致事件消费者失速,从而不得不重启应用才能会恢复。

Disruptor 3.0提供了lambda式的API。这样可以把一些复杂的操作放在Ring Buffer,所以在Disruptor3.0以后的版本最好使用Event Publisher或者Event Translator来发布事件

public class LongEventProducerWithTranslator {    // 一个translator可以看做一个事件初始化器,publicEvent方法会调用它填充Event    private static final EventTranslatorOneArg<LongEvent, ByteBuffer> TRANSLATOR = new EventTranslatorOneArg<LongEvent, ByteBuffer>() {        public void translateTo(LongEvent event, long sequence, ByteBuffer b) {            event.setValue(b.getLong(0));        }    };    private final RingBuffer<LongEvent> ringBuffer;    public LongEventProducerWithTranslator(RingBuffer<LongEvent> ringBuffer) {        this.ringBuffer = ringBuffer;    }    public void onData(ByteBuffer b) {        ringBuffer.publishEvent(TRANSLATOR, b);    }}

上面写法的另一个好处是,Translator可以分离出来并且更加容易单元测试。Disruptor提供了不同的接口(EventTranslator, EventTranslatorOneArg, EventTranslatorTwoArg, 等等)去产生一个Translator对象。很明显,Translator中方法的参数是通过RingBuffer来传递的。

最后一步就是把所有的代码组合起来完成一个完整的事件处理系统。Disruptor在这方面做了简化,使用了DSL风格的代码(其实就是按照直观的写法,不太能算得上真正的DSL)。虽然DSL的写法比较简单,但是并没有提供所有的选项。如果依靠DSL已经可以处理大部分情况了。

public class LongEventMain {    public static void main(String[] args) throws InterruptedException {        // 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<LongEvent>(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 b = ByteBuffer.allocate(8);        for (long l = 0; l<100; l++) {            b.putLong(0, l);            producer.onData(b);            //Thread.sleep(1000);        }        disruptor.shutdown();    }}

得到结果:

收到数据了:0收到数据了:1收到数据了:2收到数据了:3收到数据了:4收到数据了:5收到数据了:6收到数据了:7收到数据了:8收到数据了:9收到数据了:10收到数据了:11收到数据了:12收到数据了:13收到数据了:14收到数据了:15收到数据了:16收到数据了:17收到数据了:18收到数据了:19收到数据了:20收到数据了:21收到数据了:22收到数据了:23收到数据了:24收到数据了:25收到数据了:26收到数据了:27收到数据了:28收到数据了:29收到数据了:30收到数据了:31收到数据了:32收到数据了:33收到数据了:34收到数据了:35收到数据了:36收到数据了:37收到数据了:38收到数据了:39收到数据了:40收到数据了:41收到数据了:42收到数据了:43收到数据了:44收到数据了:45收到数据了:46收到数据了:47收到数据了:48收到数据了:49收到数据了:50收到数据了:51收到数据了:52收到数据了:53收到数据了:54收到数据了:55收到数据了:56收到数据了:57收到数据了:58收到数据了:59收到数据了:60收到数据了:61收到数据了:62收到数据了:63收到数据了:64收到数据了:65收到数据了:66收到数据了:67收到数据了:68收到数据了:69收到数据了:70收到数据了:71收到数据了:72收到数据了:73收到数据了:74收到数据了:75收到数据了:76收到数据了:77收到数据了:78收到数据了:79收到数据了:80收到数据了:81收到数据了:82收到数据了:83收到数据了:84收到数据了:85收到数据了:86收到数据了:87收到数据了:88收到数据了:89收到数据了:90收到数据了:91收到数据了:92收到数据了:93收到数据了:94收到数据了:95收到数据了:96收到数据了:97收到数据了:98收到数据了:99
0 0
原创粉丝点击