使用Disruptor实现并发编程 PPT文档

12-03-09 banq
         

2012年Qcon伦敦大会3月7日到9日在伦敦召开,所谓实践出真知,Qcon英文大会可谓是世界上战斗在实践探索第一线的顶尖高手分享大会,也是一次软件创新大会。

这次大会除了云计算架构之外,有三个具体领域:移动 Scala和Java平台,而在Java平台中,比较令人关心的是LMAX所做的Disruptor报告,DDD推动者gregyoung在其Twitter微博上说,很想去听LMAX报告,可惜挤不进去了,由此可见LMAX架构的热度。

LMAX的报告主题是:

Concurrent Programming Using The Disruptor使用Disruptor实现并发编程,主要讲述了如何使用Disruptor 2.8如何进行并发编程,Disruptor核心是其神奇的RingBuffer,见下面附图:

使用Disruptor有几个步骤,以一个生产者,一个消费者为例子:

1. 首先需要一个创建事件的工厂,实际是一种事件生产者:

private static class SimpleEventFactory implements EventFactory<SimpleEvent> { 
        public SimpleEvent newInstance() { 
            return new SimpleEvent(); 
        } 
}
<p>

其中SimpleEvent是你自己的POJO事件对象。

2.创建事件处理器,消费者获得事件后,需要激活事件处理器EventHandle:

public class SimpleEventHandler implements EventHandler<SimpleEvent>{ 
    private List<String> valuesSeen = new ArrayList<String>(); 
    @[author]Override[/author]  //这是事件处理器主要的方法,将被自动在另外一个线程激活执行
    public void onEvent(final SimpleEvent event, 
                        final long sequence, 
final boolean endOfBatch) throws Exception { 
        valuesSeen.add(event.getValue()); 
    } 
<p>

3.然后将事件,事件处理器和RingBuffer装载在一起:

//创建一个RingBuffer,事件处理器等待策略是Yield方式,比较吃CPU
final RingBuffer<SimpleEvent> ringBuffer = new RingBuffer<SimpleEvent>(SimpleEvent.EVENT_FACTORY, 
                new SingleThreadedClaimStrategy(RING_BUFFER_SIZE), new YieldingWaitStrategy()); 

final SimpleEventHandler eventHandler = new SimpleEventHandler(); 
final BatchEventProcessor<SimpleEvent> eventProcessor = 
    new BatchEventProcessor<SimpleEvent>(ringBuffer,
                                         ringBuffer.newBarrier(), 
                                         eventHandler);
eventHandler.setSequence(eventProcessor.getSequence()); 
ringBuffer.setGatingSequences(eventProcessor.getSequence());
<p>

以上装载wire到一起后,只要调用ringBuffer.publish()就在当前线程开启发送事件,SimpleEventHandler.onEvent在另外一个线程被激活运行。

这之间原理如图JDK的队列等概念,好像是线程通讯的基础用法,但是RingBuffer相比JDK提供的那些有锁Queue或LinkedList,其特点是无锁,可见本站以前关于Disruptor的帖子

该PPT中还列举了一个生产者,多个消费者的使用方式。

[该贴被banq于2012-03-10 17:43修改过]


         

27
banq
2012-03-10 17:44

同时,本站基于最新Disruptor 2.8的Jdonframework 6.5.1版本也发布了:将原来的Disruptor 2.0升级为最新的2.8版本,每个消息topic对应一个固定的RingBuffer,重用性高,能够发挥RingBuffer为CPU高速缓存优化的特点。

Disruptor 2.8支持四种WaitStrategy策略:BLOCKING是不耗CPU,但是吞吐量不大,如果你的CPU负载高了,那么切换到SLEEPING,这是CPU耗费和吞吐量之间的平衡,空闲时CPU有一定负载,Linux下比Windows平台显著;YIELDING 和 BUSY_SPIN通过大量消耗CPU,获得最大吞吐量和最低1微妙延迟性。

Jdon Framework 6.5.1缺省采取BLOCKING。

基于Jdon Framework 6.5.1的JiveJdon 4.6.1也发布,JiveJdon 4.6已经在jdon.com稳定运行三个月,7x24持续运行,没有死锁,没有内存泄漏。

下图采取Disruptor Sleep策略后的性能测试结果,虽然比较吃CPU,但是吞吐量和响应速度都比较理想:

开源Jdonframework和jivejdon下载地址:http://www.jdon.com/jdonframework/download.html

[该贴被banq于2012-03-10 19:58修改过]

banq
2012-03-10 20:07

有人可能疑问Disruptor是否有重复发明轮子的嫌疑?

这点Martin Fowler已经在其LMAX架构中有关“队列和机制偏爱的缺乏”谈过了,我个人观点是:JDK中NoBlockingLinkedList算法等虽然是经过大师Doug Lea等多年研究推出,但是算法和技术实现是两码事,LMAX团队就是认为NoBlockingLinkedList这些标准算法的API其实在实现机制上有很大缺陷,比如它们要在队列的生产者和消费者之间共享修改一些队列长度等信息,这些又造成了新锁,也就是说,虽然算法理论本身是为了减少锁或回避锁,但是为实现这个算法反而引入更多的锁。

LMAX团队还发现所谓的一些Actor模型无法满足自己应用场景的强力测试,后来发生了改进Scala的Actor模型的Why AsyncFP 引起的一场争论

不要重复发明轮子是对的,这个真知模型是有其场景边界的,如果你的应用对并发性能有无限饥渴,那么这个真知模型就不适用了。

[该贴被banq于2012-03-10 20:16修改过]

minitan
2012-03-12 15:29

有点不太明白Disruptor的使用场景,还有事件的执行状态如何获取,比如提交一个订单,我如何知道订单提交成功了呢?

又如我在论坛发布一个帖子,Disruptor不返回帖子发布的状态,而只能通过查询来确认是否发布成功?那如果在消费端发生异常了呢,是否异常也无法马上获取,只能依靠查看日志什么的?

[该贴被minitan于2012-03-12 15:40修改过]

banq
2012-03-12 15:41

2012年03月12日 15:29 "@minitan"的内容
Disruptor的使用场景 ...

作为并发非堵塞事件的底层机制,非堵塞事件应用很多,比如可以用在Socket Servce,Node.js的Socket使用的事件轮询也属于这种类型,按这里

可以用在业务逻辑处理上,大量数据需要撮合等等,比如订单处理上。

提交一个订单就产生一个订单事件,然后就可以立刻返回成功,等订单事件的消费者处理器真正处理订单了,那是还可以返回成功,当然,也不必这么来回两次,可以象Node.js那样Hold住TCP/IP那样处理。

4Go 1 2 3 4 下一页