Disruptor
背景
Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。
目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能。
参考文档
应用场景
我之前遇到一个问题场景:前端发送一个数据,通过web应用异步调用外部引擎,外部引擎对其进行相关计算操作后,外部引擎回调web应用的接口,将结果响应给web应用,web应用会将结果持久化到数据库中。
当少量请求发送时,此应用并没有问题,一旦做大量请求(批量)时,引擎运行十分缓慢。后由Jconsole
查看JVM的内存情况,发现是: 1. 并发太高导致线程推进速度缓慢 2. 内存占用太多引起的频繁GC。
每次引擎回调动作都需要启动一个线程对数据库进行插入操作,并且等待数据库返回结果,当大量回调请求涌入,线程将会瞬间大量创建。
内存占用问题是,回调的结果数据量很大,并且在插入数据库之后就不再使用该数据了,其也会发生问题。
解决方案1
解决并发太高导致线程推进速度缓慢问题
在回调接口处使用线程池进行对数据库持久化操作进行异步处理。
- 优点:并发度可控
- 缺点:
- 对象的频繁产生与回收
- 线程池底层
BlockingQueue
较Disruptor
有锁效率问题
解决方案2
使用
Disruptor
队列解决
使用多生产者多消费者模式进行,多生产者为引擎的多线程回调,多消费者为,我们使用一个WorkPool
使用多个相同逻辑的Handler
进行对事件的消费,实现多线程消费。
- 优点:
- 并发度可控
- 由于
Disruptor
的底层实现,对象的频繁产生与回收问题得到一定的解决(初始化一篇区域用于存放对象) Disuptor
效率更加高:- 伪共享问题
Disruptor
是无锁的队列,效率更高- 底层为数组的环形链表,顺序寻址,速度更快(与
ConcurrentLinkedQueue
比较,其为链表实现)
- Disruptor使用了事件监听设计模式,易于拓展新的消费者(业务逻辑),与易于组合复杂的业务逻辑
- 缺点:引入了外部依赖
用一个小Demo模拟一下场景
package pro.eddievim;
import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.concurrent.*;
/**
* @Author eddieVim
* @微信公众号 艾迪威姆 / PositiveEddie
* @Blog https://blog.csdn.net/weixin_44129784
* @Create 2021/1/25 14:06
* @Discription
*/
public class DisruptorSolution {
private static class Pack {
// 模拟大对象
private long[] buffer = new long[100000];
private String name;
private int no;
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// 用于生产者的多线程生产
private ExecutorService executor;
private int[] flag = new int[100];
// 队列
private Disruptor<Pack> disruptor;
public DisruptorSolution() {
this.executor = new ThreadPoolExecutor(5, 20,
3L, TimeUnit.MINUTES,
new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Disruptor-Producer");
}
});
// 生产者的线程工厂
ThreadFactory threadFactory = new ThreadFactory(){
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Disruptor-Consumer");
}
};
// 用于创建事件
EventFactory<Pack> eventFactory = new EventFactory<Pack>() {
@Override
public Pack newInstance() {
return new Pack();
}
};
// 阻塞策略
BlockingWaitStrategy strategy = new BlockingWaitStrategy();
// 指定RingBuffer的大小
int bufferSize = 16;
// 创建disruptor,采用多生产者模式
disruptor = new Disruptor<>(eventFactory, bufferSize, threadFactory, ProducerType.MULTI, strategy);
WorkHandler<Pack> workerHandler = new WorkHandler<Pack>() {
@Override
public void onEvent(Pack event) throws Exception {
System.out.println("\nget a pack:" + event.getNo() + "\n开始模拟入库:");
flag[event.getNo()]++;
System.out.println("入库OK啦");
}
};
disruptor.handleEventsWithWorkerPool(workerHandler, workerHandler, workerHandler);
// 启动disruptor的线程
disruptor.start();
}
/**
* 引擎
* @param pack
*/
public void callBack(Pack pack) {
// 获取disruptor中的缓冲区
RingBuffer<Pack> ringBuffer = disruptor.getRingBuffer();
// 获取该缓冲区中的下标
long sequence = ringBuffer.next();
try {
Pack obj = ringBuffer.get(sequence);
obj.setName(pack.getName());
obj.setNo(pack.getNo());
} finally {
// 发布该事件
ringBuffer.publish(sequence);
}
}
/**
* 模拟远程的引擎回调动作
* 调用callBack回调结果
*
* @throws InterruptedException
*/
public void simulateRemoteEngine() throws InterruptedException {
for (int i = 0; i < 100; i++) {
Pack pack = new Pack();
pack.setName("Name-" + i + "-name");
pack.setNo(i);
executor.submit(()->{
callBack(pack);
});
}
}
public static void main(String[] args) throws InterruptedException {
DisruptorSolution disruptorSolution = new DisruptorSolution();
// 模拟远程发送
disruptorSolution.simulateRemoteEngine();
// 等待
Thread.sleep(5000);
if (disruptorSolution.check()) {
System.out.println("\n\nNo problem! 没有并发问题!");
}
disruptorSolution.executor.shutdown();
disruptorSolution.disruptor.shutdown();
}
private boolean check() {
int count = 0;
for (int i = 0; i < 100; i++) {
if (this.flag[i] >= 2) {
System.out.println("有并发问题!!index:" + i);
} else {
count++;
}
}
return count == 100;
}
}