简介
说到Disruptor,首先需要谈谈LMAX。它是欧洲第一家也是唯一一家采用多边交易设施Multilateral Trading Facility(MTF)拥有交易所牌照和经纪商牌照的欧洲顶级金融公司。它们所构建的金融交易平台,建立在JVM平台上, 能够以很低的延迟(latency)产生大量交易(吞吐量)。 这个系统的核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单. 这个业务逻辑处理器完全是运行在内存中(in-memory),使用事件源驱动方式(event sourcing)运行,这个业务逻辑处理器的核心是Disruptor。
从功能上讲Disruptor的功能与JDK中的BlockingQueue相似,但比BlockingQueue更加强大。通过Disruptor可以高效的实现线程间的消息通信。Disruptor主要有以下特点:
-
基于生产者/消费者模型,但支持多生产者/多消费者、广播、组播等模式。
-
为事件预先分配内存
-
支持多种等待策略,并且支持可选的无锁并发算法。
Disruptor的相关概念
Event事件
从生产者传递给消费者的数据单位,可以是任意的java bean。用以表示某一事件,并可存储相关业务参数。该bean由使用者根据自己的需求创建。
EventFactory事件工厂
用来创建的Event事件对象,由使用者实现。需要实现disruptor中的EventFactory接口,Disruptor会通过该工厂来实现为事件预分配内存(即在初始化disruptor时就创建好所有事件对象)的操作。
EventHandle事件消费者
事件的消费者,需要实现Disruptor的EventHandle接口。
WaitStrategy等待策略
所谓等待策略就是Disruptor的消费者在空闲状态下采用何种方式等待任务,不同策略在不同的场景下都有不同的性能表现。在Disruptor中提供了四种等待策略
-
BlockingWaitStrategy 阻塞等待策略
BlockingWaitStrategy是disruptor的默认消费者等待策略。BlockingWaitStrategy内部使用的是典型的锁机制,来处理消费者线程的唤醒。 这种策略是所有等待策略中最慢的一种,但是也是最保守使用消耗cpu的一种用法,并且在不同的部署环境下最能保持性能一致。
-
SleepingWaitStrategy 休眠等待策略
像BlockingWaitStrategy一样,SleepingWaitStrategy也是属于一种保守使用cpu的策略。 它使用一个简单的loop繁忙等待循环,但是在循环体中间它调用了LockSupport.parkNanos(1)。 通常在linux系统这样会使得线程停顿大约60微秒。但是这样做的好处是,生产者线程不需要额外的动作去累加计数器,也不需要产生条件变量信号量开销。 但是这样带来的负面影响是,在生产者线程与消费者线程之间传递event的延迟变高了。所以SleepingWaitStrategy适合在不需要低延迟, 但需要很低的生产者线程影响的情形。一个典型的案例就是异步日志记录功能。
-
YieldingWaitStrategy 服从等待策略
YieldingWaitStrategy是2种可以用于低延迟系统的等待策略之一,充分使用压榨cpu来达到降低延迟的目标。它不断的循环等待事件。 但是,每100次循环会调用一次Thread.yield()让渡CPU执行权来允许其他的排队线程执行。 这是一种在需要极高性能并且event handler线程数少于cpu逻辑内核数的时候推荐使用的策略。
-
BusySpinWaitStrategy 繁忙旋转等待策略
BusySpinWaitStrategy是性能最高的等待策略,他需要消耗大量的CPU资源,它同样是通过不断的循环来等待事件,但与YieldingWaitStrategy相比,它少了让渡CPU执行权的操作,也就是说该等待策略会让消费者线程一直霸占着CPU。
Ring Buffer 环形队列
Disruptor最主要的组件,负责存储和更新事件对象。它是一个2的N次方大小的有界队列,
Disruptor的简单使用
使用disruptor可以分为以下几个主要步骤:
-
定义一个Event事件类,用于描述事件,该类为普通的javabean 不需要实现Disruptor的任何接口
-
定义一个EventFactory 事件工厂类,需实现Disruptor的EventFactory接口,Disruptor会通过该工厂类预创建事件对象。
-
定义一个EventHandle 事件处理类(事件消费者),需实现Disruptor的EventHandle接口。
-
创建Distuptor对象,将上述的定义的bean初始化并注册到Disruptor对象中。
-
通过Distuptor对象或者RingBuffer对象的publishEvent、publish方法发布事件。(RingBuffer是实际处理事件发布和事件存在的组件)
如下是一个简单的单生产者,多消费者的代码示例。
maven配置
com.lmax
disruptor
3.4.2
Event
import lombok.Data;
/**
* 订单回调事件
*
* @version OrderEvent.java, v 0.1 2018-12-25 21:28
*/
@Data
public class OrderCallBackEvent {
private String orderSn;
}
EventFactory
import com.lmax.disruptor.EventFactory;
/**
* 订单回调事件工厂
*
* @version OrderCallBackFactory.java, v 0.1 2018-12-25 21:30
*/
public class OrderCallBackFactory implements EventFactory {
@Override
public OrderCallBackEvent newInstance() {
// disruptor通过该事件工厂为事件预分配内存空间并创建空的事件对象
return new OrderCallBackEvent();
}
}
EventHandler
import com.lmax.disruptor.EventHandler;
/**
* 订单回调事件消费 - 开放平台通知
*
* @version OpenNotifiyHandle.java, v 0.1 2018-12-25 21:53
*/
public class OpenNotifiyHandle implements EventHandler {
@Override
public void onEvent(OrderCallBackEvent event, long sequence, boolean endOfBatch) throws Exception {
String orderSn = event.getOrderSn();
System.out.println("当前线程:" + Thread.currentThread().getName() + " ,OpenNotifiyHandle == orderSn:" +orderSn + " ,sequence:" + sequence + " ,endOfBatch:" + endOfBatch);
}
}
import com.lmax.disruptor.EventHandler;
/**
* 订单回调事件消费 - 微信吸粉通知
*
* @version WxFansHandle.java, v 0.1 2018-12-25 21:58
*/
public class WxFansHandle implements EventHandler {
@Override
public void onEvent(OrderCallBackEvent event, long sequence, boolean endOfBatch) throws Exception {
String orderSn = event.getOrderSn();
System.out.println("当前线程:" + Thread.currentThread().getName() + " ,WxFansHandle == orderSn:" +orderSn + " ,sequence:" + sequence + " ,endOfBatch:" + endOfBatch);
}
}
OrderCallBackMain
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.lmax.disruptor.util.DaemonThreadFactory;
/**
* 订单回调入口
*
* @version OrderCallBackMain.java, v 0.1 2018-12-25 21:39
*/
public class OrderCallBackMain {
/**
* ring buffer的大小必须设置为2的指数倍,主要是为了将求模运算转为&运算提高效率
*/
static int RING_BUFFER_SIZE = 1024;
public static void main(String[] args) throws InterruptedException {
// 订单事件工厂,用于预创建订单事件
OrderCallBackFactory orderCallBackFactory = new OrderCallBackFactory();
// 初始化disruptor
// orderCallBackFactory: 事件工厂
// RING_BUFFER_SIZE: 环形队列的大小,必须是2的指数倍
// DaemonThreadFactory.INSTANCE: 线程工厂, disruptor会通过线程工程来创建消费者线程,默认每个消费者只会创建一个消费者线程
//
Disruptor disruptor = new Disruptor<>(orderCallBackFactory, RING_BUFFER_SIZE, DaemonThreadFactory.INSTANCE, ProducerType.MULTI, new BlockingWaitStrategy());
// disruptor默认支持广播模式,即同一个消息,所有注册的消费者都会消费一次
// 注册开放平台回调处、注册微信吸粉微信吸粉
disruptor.handleEventsWith(new OpenNotifiyHandle(), new WxFansHandle());
// 启动disruptor
disruptor.start();
// 发布订单事件
// disruptor会通过我们设置的事件工厂预先帮我创建好事件对象
// 所以我们在发布事件时只需要为我们的event对象写入数据
disruptor.publishEvent((event, sequence) -> {
event.setOrderSn("123456");
});
// 发布订单事件
disruptor.publishEvent((event, sequence) -> {
event.setOrderSn("9999");
});
// 另一种发布事件的方式,与上面的方式等价
RingBuffer ringBuffer = disruptor.getRingBuffer();
long sequence = ringBuffer.next();
OrderCallBackEvent event = ringBuffer.get(sequence);
event.setOrderSn("1234856");
ringBuffer.publish(sequence);
}
}