秒杀架构持续优化中,基于自身认知不足之处在所不免,也请你们指正,共同进步。文章标题来自码友的建议,但愿能够把阻塞队列ArrayBlockingQueue这个队列替换成Disruptor,因为以前曾接触过这个东西,据说很不错,正好借此机会整合进来。html
LMAX Disruptor是一个高性能的线程间消息库。它源于LMAX对并发性,性能和非阻塞算法的研究,现在构成了Exchange基础架构的核心部分。java
Disruptor它是一个开源的并发框架,并得到2011 Duke’s 程序框架创新奖,可以在无锁的状况下实现网络的Queue并发操做。git
Disruptor是一个高性能的异步处理框架,或者能够认为是最快的消息框架(轻量的JMS),也能够认为是一个观察者模式的实现,或者事件监听模式的实现。github
在这里你能够跟BlockingQueue队列做比对,简单的理解为它是一种高效的"生产者-消费者"模型,先了解后深刻底层原理。算法
写代码案例以前,你们最好先了解 Disruptor 的核心概念,至少知道它是如何运做的。spring
有兴趣的参考: https://coolshell.cn/articles/9169.htmlshell
https://www.cnblogs.com/daoqidelv/p/6995888.html缓存
这里以咱们系统中的秒杀做为案例,后面有相对复杂的场景介绍。网络
定义秒杀事件对象:架构
/** * 事件对象(秒杀事件) * 建立者 科帮网 */
public class SeckillEvent implements Serializable {
private static final long serialVersionUID = 1L;
private long seckillId;
private long userId;
public SeckillEvent(){
}
public long getSeckillId() {
return seckillId;
}
public void setSeckillId(long seckillId) {
this.seckillId = seckillId;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
}
复制代码
为了让Disruptor为咱们预先分配这些事件,咱们须要一个将执行构造的EventFactory:
/** * 事件生成工厂(用来初始化预分配事件对象) * 建立者 科帮网 */
public class SeckillEventFactory implements EventFactory<SeckillEvent> {
public SeckillEvent newInstance() {
return new SeckillEvent();
}
}
复制代码
而后,咱们须要建立一个处理这些事件的消费者:
/** * 消费者(秒杀处理器) * 建立者 科帮网 */
public class SeckillEventConsumer implements EventHandler<SeckillEvent> {
//业务处理、这里是没法注入的,须要手动获取,见源码
private ISeckillService seckillService = (ISeckillService) SpringUtil.getBean("seckillService");
public void onEvent(SeckillEvent seckillEvent, long seq, boolean bool) throws Exception {
seckillService.startSeckil(seckillEvent.getSeckillId(), seckillEvent.getUserId());
}
}
复制代码
既然有消费者,咱们将须要这些秒杀事件的来源:
/** * 使用translator方式生产者 * 建立者 科帮网 */
public class SeckillEventProducer {
private final static EventTranslatorVararg<SeckillEvent> translator = new EventTranslatorVararg<SeckillEvent>() {
public void translateTo(SeckillEvent seckillEvent, long seq, Object... objs) {
seckillEvent.setSeckillId((Long) objs[0]);
seckillEvent.setUserId((Long) objs[1]);
}
};
private final RingBuffer<SeckillEvent> ringBuffer;
public SeckillEventProducer(RingBuffer<SeckillEvent> ringBuffer){
this.ringBuffer = ringBuffer;
}
public void seckill(long seckillId, long userId){
this.ringBuffer.publishEvent(translator, seckillId, userId);
}
}
复制代码
最后,咱们来写一个测试类,运行一下(跑不通,须要修改消费者):
/** * 測試類 * 建立者 科帮网 */
public class SeckillEventMain {
public static void main(String[] args) {
producerWithTranslator();
}
public static void producerWithTranslator(){
SeckillEventFactory factory = new SeckillEventFactory();
int ringBufferSize = 1024;
ThreadFactory threadFactory = new ThreadFactory() {
public Thread newThread(Runnable runnable) {
return new Thread(runnable);
}
};
//建立disruptor
Disruptor<SeckillEvent> disruptor = new Disruptor<SeckillEvent>(factory, ringBufferSize, threadFactory);
//链接消费事件方法
disruptor.handleEventsWith(new SeckillEventConsumer());
//启动
disruptor.start();
RingBuffer<SeckillEvent> ringBuffer = disruptor.getRingBuffer();
SeckillEventProducer producer = new SeckillEventProducer(ringBuffer);
for(long i = 0; i<10; i++){
producer.seckill(i, i);
}
disruptor.shutdown();//关闭 disruptor,方法会堵塞,直至全部的事件都获得处理;
}
}
复制代码
这里举一个你们平常的例子,停车场景。当汽车进入停车场时(A),系统首先会记录汽车信息(B)。同时也会发送消息到其余系统处理相关业务(C),最后发送短信通知车主收费开始(D)。
一个生产者A与三个消费者B、C、D,D的事件处理须要B与C先完成。则该模型结构以下:
在这个结构下,每一个消费者拥有各自独立的事件序号Sequence,消费者之间不存在共享竞态。SequenceBarrier1监听RingBuffer的序号cursor,消费者B与C经过SequenceBarrier1等待可消费事件。SequenceBarrier2除了监听cursor,同时也监听B与C的序号Sequence,从而将最小的序号返回给消费者D,由此实现了D依赖B与C的逻辑。
代码案例:从0到1构建分布式秒杀系统
参考: https://github.com/LMAX-Exchange/disruptor/wiki
https://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started
http://wiki.jikexueyuan.com/project/disruptor-getting-started/lmax-framework.html