秒杀架构持续优化中,基于自身认知不足之处在所难免,也请大家指正,共同进步。文章标题来自码友的建议,希望可以把阻塞队列ArrayBlockingQueue这个队列替换成Disruptor,由于之前曾接触过这个东西,听说很不错,正好借此机会整合进来。
LMAX Disruptor是一个高性能的线程间消息库。它源于LMAX对并发性,性能和非阻塞算法的研究,如今构成了Exchange基础架构的核心部分。
Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。
Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。
在这里你可以跟BlockingQueue队列作比对,简单的理解为它是一种高效的"生产者-消费者"模型,先了解后深入底层原理。
写代码案例之前,大家最好先了解 Disruptor 的核心概念,至少知道它是如何运作的。
有兴趣的参考: https://coolshell.cn/articles/9169.html
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