秒杀架构持续优化中,基于自身认知不足之处在所不免,也请你们指正,共同进步。文章标题来自码友<tukangzheng>的建议,但愿能够把阻塞队列ArrayBlockingQueue这个队列替换成Disruptor,因为以前曾接触过这个东西,据说很不错,正好借此机会整合进来。html
LMAX Disruptor是一个高性能的线程间消息库。它源于LMAX对并发性,性能和非阻塞算法的研究,现在构成了Exchange基础架构的核心部分。面试
Disruptor它是一个开源的并发框架,并得到2011 Duke’s 程序框架创新奖,可以在无锁的状况下实现网络的Queue并发操做。算法
在这里你能够跟BlockingQueue队列做比对,简单的理解为它是一种高效的"生产者-消费者"模型,先了解后深刻底层原理。shell
写代码案例以前,你们最好先了解 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的逻辑。
欢迎工做一到五年的 Java 的工程师朋友们加入的 Java 架构开发:6851-67-672
本群提供免费的学习指导架构资料以及免费的解答
不懂得问题均可以在本群提出来以后还会有职业生涯规划以及面试指导