Disruptor快速入门
在JDK的多线程与并发库一文中, 提到了BlockingQueue实现了生产者-消费者模型
BlockingQueue是基于锁实现的, 而锁的效率通常较低. 有没有使用CAS机制实现的生产者-消费者?
Disruptor就是这样.
disruptor使用观察者模式, 主动将消息发送给消费者, 而不是等消费者从队列中取; 在无锁的情况下, 实现queue(环形, RingBuffer)的并发操作, 性能远高于BlockingQueue
1.生产者-消费者
1.1使用Disruptor类
RingBuffer通过Disruptor实例获得
public class Client { public static void main(String[] args) throws Exception { //1.配置并获得Disruptor
ExecutorService executor = Executors.newCachedThreadPool();
LongEventFactory factory = new LongEventFactory();
// 设置RingBuffer大小, 需为2的N次方(能将求模运算转为位运算提高效率 ), 否则影响性能
int ringBufferSize = 1024 * 1024; //创建disruptor, 泛型参数:传递的事件的类型
// 第一个参数: 产生Event的工厂类, Event封装生成-消费的数据
// 第二个参数: RingBuffer的缓冲区大小
// 第三个参数: 线程池
// 第四个参数: SINGLE单个生产者, MULTI多个生产者
// 第五个参数: WaitStrategy 当消费者阻塞在SequenceBarrier上, 消费者如何等待的策略.
//BlockingWaitStrategy 使用锁和条件变量, 效率较低, 但CPU的消耗最小, 在不同部署环境下性能表现比较一致
//SleepingWaitStrategy 多次循环尝试不成功后, 让出CPU, 等待下次调度; 多次调度后仍不成功, 睡眠纳秒级别的时间再尝试. 平衡了延迟和CPU资源占用, 但延迟不均匀.
//YieldingWaitStrategy 多次循环尝试不成功后, 让出CPU, 等待下次调度. 平衡了延迟和CPU资源占用, 延迟也比较均匀.
//BusySpinWaitStrategy 自旋等待,类似自旋锁. 低延迟但同时对CPU资源的占用也多.
Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(factory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy());
// 注册事件消费处理器, 也即消费者. 可传入多个EventHandler ...
disruptor.handleEventsWith(new LongEventHandler());
// 启动
disruptor.start(); //2.将数据装入RingBuffer
RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
// 创建生产者, 以下方式一使用原始api, 方式二使用新API
//LongEventProducer producer = new LongEventProducer(ringBuffer);
LongEventProducerWithTranslator producer = new LongEventProducerWithTranslator(ringBuffer); ByteBuffer byteBuffer = ByteBuffer.allocate(8); // 这里只是笔者实验, 不是必须要用ByteBuffer保存long数据
for(int i = 0; i < 100; ++i){
byteBuffer.putLong(0, i);
producer.produceData(byteBuffer);
} disruptor.shutdown(); //关闭 disruptor 阻塞直至所有事件都得到处理
executor.shutdown(); // 需关闭 disruptor使用的线程池, 上一步disruptor关闭时不会连带关闭线程池
}
}
// Event封装要传递的数据
public class LongEvent {
private long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
// 产生Event的工厂
public class LongEventFactory implements EventFactory {
@Override
public Object newInstance() {
return new LongEvent();
}
}
public class LongEventHandler implements EventHandler<LongEvent> {
// 消费逻辑
@Override
public void onEvent(LongEvent longEvent, long l, boolean b) throws Exception {
System.out.println(longEvent.getValue());
}
}
//生产者实现一
public class LongEventProducer {
// 生产者持有RingBuffer的引用
private final RingBuffer<LongEvent> ringBuffer; public LongEventProducer(RingBuffer<LongEvent> ringBuffer){
this.ringBuffer = ringBuffer;
} public void produceData(ByteBuffer bb){
// 获得下一个Event槽的下标
long sequence = ringBuffer.next();
try {
// 给Event填充数据
LongEvent event = ringBuffer.get(sequence);
event.setValue(bb.getLong(0));
} finally {
// 发布Event, 激活观察者去消费, 将sequence传递给该消费者
//publish应该放在 finally块中以确保一定会被调用->如果某个事件槽被获取但未提交, 将会堵塞后续的publish动作。
ringBuffer.publish(sequence);
}
}
}
//生产者实现二
public class LongEventProducerWithTranslator { // 使用EventTranslator, 封装 获取Event的过程
private static final EventTranslatorOneArg<LongEvent, ByteBuffer> TRANSLATOR = new EventTranslatorOneArg<LongEvent, ByteBuffer>() {
@Override
public void translateTo(LongEvent event, long sequeue, ByteBuffer buffer) {
event.setValue(buffer.getLong(0));
}
}; private final RingBuffer<LongEvent> ringBuffer; public LongEventProducerWithTranslator(RingBuffer<LongEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
} public void produceData(ByteBuffer buffer){
// 发布
ringBuffer.publishEvent(TRANSLATOR, buffer);
}
}
1.2 直接使用RingBuffer
给出了两种方式:EventProcessor与WorkPool(可处理多消费者)
public class ClientForEventProcessor { public static void main(String[] args) throws Exception {
int BUFFER_SIZE = 1024;
int THREAD_NUMBERS = 4; // 这里直接获得RingBuffer. createSingleProducer创建一个单生产者的RingBuffer // 第一个参数为EventFactory,产生数据Trade的工厂类
// 第二个参数是RingBuffer的大小,需为2的N次方
// 第三个参数是WaitStrategy, 消费者阻塞时如何等待生产者放入Event
final RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(new EventFactory<Trade>() {
@Override
public Trade newInstance() {
return new Trade(UUID.randomUUID().toString());
}
}, BUFFER_SIZE, new YieldingWaitStrategy()); //SequenceBarrier, 协调消费者与生产者, 消费者链的先后顺序. 阻塞后面的消费者(没有Event可消费时)
SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(); //创建事件处理器 (消费者): 处理ringBuffer, 用TradeHandler的方法处理(实现EventHandler), 用sequenceBarrier协调生成-消费
//如果存在多个消费者(老api, 可用workpool解决) 那重复执行 创建事件处理器-注册进度-提交消费者的过程, 把其中TradeHandler换成其它消费者类
BatchEventProcessor<Trade> transProcessor = new BatchEventProcessor<Trade>(ringBuffer, sequenceBarrier, new TradeHandler());
//把消费者的消费进度情况注册给RingBuffer结构(生产者) !如果只有一个消费者的情况可以省略
ringBuffer.addGatingSequences(transProcessor.getSequence()); //创建线程池
ExecutorService executors = Executors.newFixedThreadPool(THREAD_NUMBERS);
//把消费者提交到线程池, !说明EventProcessor实现了callable接口
executors.submit(transProcessor); // 生产者, 这里新建线程不是必要的
Future<?> future= executors.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
long seq;
for (int i = 0; i < 10; i++) {
seq = ringBuffer.next();
ringBuffer.get(seq).setPrice(Math.random() * 9999);
ringBuffer.publish(seq);
}
return null;
}
}); Thread.sleep(1000); //等上1秒,等待消费完成
transProcessor.halt(); //通知事件处理器 可以结束了(并不是马上结束!)
executors.shutdown();
}
}
public class ClientForWorkPool {
public static void main(String[] args) throws InterruptedException {
int BUFFER_SIZE = 1024;
int THREAD_NUMBERS = 4; RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(new EventFactory<Trade>() {
public Trade newInstance() {
return new Trade(UUID.randomUUID().toString());
}
}, BUFFER_SIZE); SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(); // 第三个参数: 异常处理器, 这里用ExceptionHandler; 第四个参数WorkHandler的实现类, 可为数组(即传入多个消费者)
WorkerPool<Trade> workerPool = new WorkerPool<Trade>(ringBuffer, sequenceBarrier, new IgnoreExceptionHandler(), new TradeHandler()); ExecutorService executors = Executors.newFixedThreadPool(THREAD_NUMBERS);
workerPool.start(executors); // 生产10个数据
for (int i = 0; i < 8; i++) {
long seq = ringBuffer.next();
ringBuffer.get(seq).setPrice(Math.random() * 9999);
ringBuffer.publish(seq);
} Thread.sleep(1000);
workerPool.halt();
executors.shutdown();
}
}
// 封装交易数据
public class Trade {
private String id; // 订单ID
private String name;
private double price; // 金额 public Trade(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
// 消费者, 这里实现一个接口就行, 写两个是为了同时测试EventProcessor和WorkPool
public class TradeHandler implements EventHandler<Trade>, WorkHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
this.onEvent(event);
}
@Override
public void onEvent(Trade event) throws Exception {
//具体的消费逻辑
System.out.println(event.getId());
}
}
1.3 多生产者-多消费者
一个Event只能被某一个消费者处理
public static void main(String[] args) throws Exception {
//创建RingBuffer
RingBuffer<Order> ringBuffer =
RingBuffer.create(ProducerType.MULTI,
new EventFactory<Order>() {
@Override
public Order newInstance() {
return new Order();
}
},
1024 * 1024, new YieldingWaitStrategy()); SequenceBarrier barriers = ringBuffer.newBarrier(); Consumer[] consumers = new Consumer[3];
for(int i = 0; i < consumers.length; i++){
consumers[i] = new Consumer("ct" + i);
}
// 3个消费者
WorkerPool<Order> workerPool = new WorkerPool<Order>(ringBuffer, barriers, new MyExceptionHandler(), consumers); ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
workerPool.start(executors);
// 10个生产者, 每个生成者生产20个数据
for (int i = 0; i < 10; i++) {
final Producer p = new Producer(ringBuffer);
new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 2; j++){
p.produceData(UUID.randomUUID().toString());
}
}
}).start();
} System.out.println("----开始生产----");
Thread.sleep(1000); // 等待消费完成
System.out.println("总共消费数量:" + consumers[0].getCount() ); workerPool.halt();
executors.shutdown();
} static class MyExceptionHandler implements ExceptionHandler {
public void handleEventException(Throwable ex, long sequence, Object event) {}
public void handleOnStartException(Throwable ex) {}
public void handleOnShutdownException(Throwable ex) {}
}
}
public class Consumer implements WorkHandler<Order>{ private String consumerId;
// 消费计数器
private static AtomicInteger count = new AtomicInteger(0); public Consumer(String consumerId){
this.consumerId = consumerId;
} @Override
public void onEvent(Order order) throws Exception {
System.out.println("当前消费者: " + this.consumerId + ", 消费信息: " + order.getId());
count.incrementAndGet();
} public int getCount(){
return count.get();
}
}
public class Producer { private final RingBuffer<Order> ringBuffer;
public Producer(RingBuffer<Order> ringBuffer){
this.ringBuffer = ringBuffer;
}
public void produceData(String data){
long sequence = ringBuffer.next();
try {
Order order = ringBuffer.get(sequence);
order.setId(data);
} finally {
ringBuffer.publish(sequence);
}
}
}
public class Order {
private String id;
private String name;
private double price; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
2. 并行处理
除了实现生产者-消费者模型, Disruptor还可以进行多路并行处理(一个Event可以进入多个路径同时进行处理, 因为不同路径操作的是同一个Event, 路径可以汇合)
public class Client {
public static void main(String[] args) throws InterruptedException { long beginTime=System.currentTimeMillis();
int bufferSize=1024;
ExecutorService executor=Executors.newFixedThreadPool(7); // 注意: 线程数>=handler数+1 Disruptor<Trade> disruptor = new Disruptor<Trade>(
new EventFactory<Trade>() {
@Override
public Trade newInstance() {
return new Trade(UUID.randomUUID().toString());
}
}, bufferSize, executor, ProducerType.SINGLE, new BusySpinWaitStrategy());
// 菱形操作
/*
// 创建消费者组(含H1,H2) H1,H2并行执行无先后顺序
EventHandlerGroup<Trade> handlerGroup = disruptor.handleEventsWith(new Handler1(), new Handler2());
// C1,C2都完成后执行C3, 像JMS传递消息
handlerGroup.then(new Handler3());
*/ // 顺序操作
/*
disruptor.handleEventsWith(new Handler1()).handleEventsWith(new Handler2()).handleEventsWith(new Handler3());
*/ // 六边形操作. H1, H4串行执行; H2, H5串行执行; 而H1,H4 与 H2,H5 并行执行
Handler1 h1 = new Handler1();
Handler2 h2 = new Handler2();
Handler3 h3 = new Handler3();
Handler4 h4 = new Handler4();
Handler5 h5 = new Handler5();
disruptor.handleEventsWith(h1, h2);
disruptor.after(h1).handleEventsWith(h4);
disruptor.after(h2).handleEventsWith(h5);
disruptor.after(h4, h5).handleEventsWith(h3); disruptor.start();
// 启动生产线程
executor.submit(new TradePublisher(disruptor));
Thread.sleep(1000); // 等待消费完成 disruptor.shutdown();
executor.shutdown();
System.out.println("总耗时:"+(System.currentTimeMillis()-beginTime));
}
}
public class TradePublisher implements Runnable { private Disruptor<Trade> disruptor;
private static final int LOOP = 100;// 模拟百次交易的发生 public TradePublisher(Disruptor<Trade> disruptor) {
this.disruptor = disruptor;
} @Override
public void run() {
TradeEventTranslator tradeTransloator = new TradeEventTranslator();
for (int i = 0; i < LOOP; i++) {
disruptor.publishEvent(tradeTransloator);
}
}
} class TradeEventTranslator implements EventTranslator<Trade> {
private Random random = new Random();
@Override
public void translateTo(Trade event, long sequence) {
this.generateTrade(event);
}
private Trade generateTrade(Trade trade) {
trade.setPrice(random.nextDouble() * 9999);
return trade;
}
}
public class Handler1 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch)
throws Exception {
System.out.println("handler1: set name");
event.setName("h1");
}
}
public class Handler2 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler2: set price");
event.setPrice(17.0);
}
}
public class Handler3 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler3: name: " + event.getName() + " , price: " + event.getPrice() + "; instance: " + event.getId());
}
}
public class Handler4 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler4: append name");
event.setName(event.getName() + "h4");
}
}
public class Handler5 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler5: add price");
event.setPrice(event.getPrice() + 3.0);
}
}
Disruptor快速入门的更多相关文章
- 无锁并发框架Disruptor学习入门
刚刚听说disruptor,大概理一下,只为方便自己理解,文末是一些自己认为比较好的博文,如果有需要的同学可以参考. 本文目标:快速了解Disruptor是什么,主要概念,怎么用 1.Disrupto ...
- Disruptor 系列(一)快速入门
Disruptor 系列(一)快速入门 Disruptor:是一个开源的并发框架,能够在 无锁 的情况下实现网络的 Queue 并发操作,所以处理数据的能力比 Java 本身提供的并发类容器要大的多, ...
- Web Api 入门实战 (快速入门+工具使用+不依赖IIS)
平台之大势何人能挡? 带着你的Net飞奔吧!:http://www.cnblogs.com/dunitian/p/4822808.html 屁话我也就不多说了,什么简介的也省了,直接简单概括+demo ...
- SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)
SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...
- 前端开发小白必学技能—非关系数据库又像关系数据库的MongoDB快速入门命令(2)
今天给大家道个歉,没有及时更新MongoDB快速入门的下篇,最近有点小忙,在此向博友们致歉.下面我将简单地说一下mongdb的一些基本命令以及我们日常开发过程中的一些问题.mongodb可以为我们提供 ...
- 【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- 【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- Mybatis框架 的快速入门
MyBatis 简介 什么是 MyBatis? MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果 ...
- grunt快速入门
快速入门 Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器. Grunt 0.4.x 必须配合Node.js >= 0.8.0版本使用.:奇数版本 ...
随机推荐
- ruby中的instance_eval,class_eval,eval
ruby具有在运行时执行以字符串形式保存的代码的功能设施,eval族方法 .包括Kernel#eval,Object#instance_eval,Module#class_eval. Kernel#e ...
- AtCoder Regular Contest 080 E - Young Maids
地址:http://arc080.contest.atcoder.jp/tasks/arc080_c 题目: E - Young Maids Time limit : 2sec / Memory li ...
- pigeon 介绍
https://github.com/dianping/pigeon Pigeon开发指南 Pigeon是一个分布式服务通信框架(RPC),在美团点评内部广泛使用,是美团点评最基础的底层框架之一. 主 ...
- zookeeper 监听事件 PathChildrenCacheListener
zookeeper 监听事件 PathChildrenCacheListener PathChildrenCacheListener一次父节点注册,监听每次子节点操作,不监听自身和查询. 1.测试类: ...
- netty11---管道
客户端: package com.server; import java.net.Socket; public class Client { public static void main(Strin ...
- C++之图片旋转90,再保存
下面测试代码只需要全部放在一个.cpp文件里就行 //#include "stdafx.h"#include <stdio.h>#include <string& ...
- 微信开放平台--》网站应用开发 微信登录网站接口(https://open.weixin.qq.com/)
地址:https://open.weixin.qq.com/ 手册:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&am ...
- 寻路算法A*, JPS(跳点搜索)的一些杂谈
A*是一个比较经典的启发式寻路算法.是基于dijkstra算法,但是加入了启发函数,使路径搜索效率更高.实现起来很简单.不过要做到通用性高,比如支持各种不同类型的地图,甚至不仅仅是地图,而是个图结构如 ...
- 20145329 吉东云《Java程序设计》第二周学习总结
教材学习内容总结 第三章 基础语法 基本类型 1.整数(short.int.long) 2.字节(byte),可表示-128~127的整数 3.浮点数(float/double),主要储存小数数值 4 ...
- windows 下获取父进程pid
DWORD GetParentProcessID(DWORD dwProcessId) { LONG status; DWORD dwParentPID = (DWORD)-1; HANDLE hPr ...