本文共 5096 字,大约阅读时间需要 16 分钟。
秒杀架构持续优化中,基于自身认知不足之处在所难免,也请大家指正,共同进步。文章标题来自码友<tukangzheng>的建议,希望可以把阻塞队列ArrayBlockingQueue这个队列替换成Disruptor,由于之前曾接触过这个东西,听说很不错,正好借此机会整合进来。
LMAX Disruptor是一个高性能的线程间消息库。它源于LMAX对并发性,性能和非阻塞算法的研究,如今构成了Exchange基础架构的核心部分。
Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。
在这里你可以跟BlockingQueue队列作比对,简单的理解为它是一种高效的"生产者-消费者"模型,先了解后深入底层原理。
写代码案例之前,大家最好先了解 Disruptor 的核心概念,至少知道它是如何运作的。
有兴趣的参考:
这里以我们系统中的秒杀作为案例,后面有相对复杂的场景介绍。
定义秒杀事件对象:
/** * 事件对象(秒杀事件) * 创建者 科帮网 */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{ public SeckillEvent newInstance() { return new SeckillEvent(); }}
然后,我们需要创建一个处理这些事件的消费者:
/** * 消费者(秒杀处理器) * 创建者 科帮网 */public class SeckillEventConsumer implements EventHandler{ //业务处理、这里是无法注入的,需要手动获取,见源码 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 EventTranslatorVarargtranslator = new EventTranslatorVararg () { public void translateTo(SeckillEvent seckillEvent, long seq, Object... objs) { seckillEvent.setSeckillId((Long) objs[0]); seckillEvent.setUserId((Long) objs[1]); } }; private final RingBuffer ringBuffer; public SeckillEventProducer(RingBuffer 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 Disruptordisruptor = new Disruptor (factory, ringBufferSize, threadFactory); //连接消费事件方法 disruptor.handleEventsWith(new SeckillEventConsumer()); //启动 disruptor.start(); RingBuffer 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的逻辑。
代码案例:
参考:
转载于:https://blog.51cto.com/itstyle/2121926