欢迎访问我的GitHub

https://github.com/zq2599/blog_demos

内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;

《disruptor笔记》系列链接

  1. 快速入门
  2. Disruptor类分析
  3. 环形队列的基础操作(不用Disruptor类)
  4. 事件消费知识点小结
  5. 事件消费实战
  6. 常见场景
  7. 等待策略
  8. 知识点补充(终篇)

本篇概览

  • 本文是《disruptor笔记》系列的第六篇,主要内容是将一些常用的消费模式做汇总,后续日常开发中如果有需要就能拿来即用;
  • 以下是常用的模式:
  1. 多个消费者独立消费,前文已实现,本篇跳过
  2. 多个消费者共同消费,前文已实现,本篇跳过
  3. 既有独立消费,也有共同消费,前文已实现,本篇跳过
  4. 多个生产者和多个独立消费者:

  1. C1、C2独立消费,C3依赖C1和C2

  1. C1独立消费,C2和C3也独立消费,但依赖C1,C4依赖C2和C3:

  1. C1和C2独立消费,C3和C4也是独立消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4:

  1. C1和C2共同消费,C3和C4也是共同消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4:

  1. C1和C2共同消费,C3和C4独立消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4:

  1. C1和C2独立消费,C3和C4是共同消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4:

关于本篇代码

  • 为了省事儿,本次不会新建工程,而是直接使用前文的consume-mode模块,因此,下面这些类直接就直接使用了,无需重写代码:
  1. 事件定义:OrderEvent
  2. 事件工厂:OrderEventFactory
  3. 事件生产者:OrderEventProducer
  4. 用在独立消费场景的事件消费者:MailEventHandler
  5. 用在共同消费场景的事件消费者:MailWorkHandler

源码下载

名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本次实战的源码在disruptor-tutorials文件夹下,如下图红框所示:

  • disruptor-tutorials是个父工程,里面有多个module,本篇实战的module是consume-mode,如下图红框所示:

多个生产者和多个独立消费者

咱们即将实现下图的逻辑:

  • 前面几篇文章所有实战的生产者都只有一个,到了本篇,为了让consume-mode模块的代码能够支持多生产者,咱们要对功能业务的抽象父类做以下两处改动:
  1. init方法原本为private型,现在为了能让子类重此方法,将其改为protected类型;

  2. 增加名为publishWithProducer2的方法,可见内部只有抛出异常,要想其正常工作,需要子类自己来实现:

  1. public void publishWithProducer2(String value) throws Exception {
  2. throw new Exception("父类未实现此方法,请在子类中重写此方法后再调用");
  3. }
  • 为了实现多生产者功能,新增MultiProducerServiceImpl.java,有几处要注意的地方稍后会提到:
  1. package com.bolingcavalry.service.impl;
  2. import com.bolingcavalry.service.*;
  3. import com.lmax.disruptor.BlockingWaitStrategy;
  4. import com.lmax.disruptor.dsl.Disruptor;
  5. import com.lmax.disruptor.dsl.ProducerType;
  6. import lombok.Setter;
  7. import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
  8. import org.springframework.stereotype.Service;
  9. import javax.annotation.PostConstruct;
  10. @Service("multiProducerService")
  11. public class MultiProducerServiceImpl extends ConsumeModeService {
  12. /**
  13. * 第二个生产者
  14. */
  15. @Setter
  16. protected OrderEventProducer producer2;
  17. @PostConstruct
  18. @Override
  19. protected void init() {
  20. // 实例化
  21. disruptor = new Disruptor<>(new OrderEventFactory(),
  22. BUFFER_SIZE,
  23. new CustomizableThreadFactory("event-handler-"),
  24. // 生产类型是多生产者
  25. ProducerType.MULTI,
  26. // BlockingWaitStrategy是默认的等待策略
  27. new BlockingWaitStrategy());
  28. // 留给子类实现具体的事件消费逻辑
  29. disruptorOperate();
  30. // 启动
  31. disruptor.start();
  32. // 第一个生产者
  33. setProducer(new OrderEventProducer(disruptor.getRingBuffer()));
  34. // 第二个生产者
  35. setProducer2(new OrderEventProducer(disruptor.getRingBuffer()));
  36. }
  37. @Override
  38. protected void disruptorOperate() {
  39. // 一号消费者
  40. MailEventHandler c1 = new MailEventHandler(eventCountPrinter);
  41. // 二号消费者
  42. MailEventHandler c2 = new MailEventHandler(eventCountPrinter);
  43. // 调用handleEventsWithWorkerPool,表示创建的多个消费者以共同消费的模式消费
  44. disruptor.handleEventsWith(c1, c2);
  45. }
  46. @Override
  47. public void publishWithProducer2(String value) throws Exception {
  48. producer2.onData(value);
  49. }
  50. }
  • 上述代码有以下几处要注意:
  1. 重写父类的init方法,主要是实例化Disruptor的时候,多传入两个参数:ProducerType.MULTI表示生产类型是多生产者,BlockingWaitStrategy是等待策略,之前的代码中咱们没有传此参数时,默认的就是BlockingWaitStrategy
  2. init方法中还执行了setProducer2方法,设置成员变量producer2
  3. 重写publishWithProducer2方法,调用成员变量producer2发表事件
  4. 重写disruptorOperate方法,里面设置了两个独立消费者
  • 验证上述代码的方式依旧是单元测试,打开ConsumeModeServiceTest.java,新增以下代码,可见新增了两个线程同时执行发布事件的操作:
  1. @Autowired
  2. @Qualifier("multiProducerService")
  3. ConsumeModeService multiProducerService;
  4. @Test
  5. public void testMultiProducerService() throws InterruptedException {
  6. log.info("start testMultiProducerService");
  7. CountDownLatch countDownLatch = new CountDownLatch(1);
  8. // 两个生产者,每个生产100个事件,一共生产两百个事件
  9. // 两个独立消费者,每人消费200个事件,因此一共消费400个事件
  10. int expectEventCount = EVENT_COUNT*4;
  11. // 告诉service,等消费到400个消息时,就执行countDownLatch.countDown方法
  12. multiProducerService.setCountDown(countDownLatch, expectEventCount);
  13. // 启动一个线程,用第一个生产者生产事件
  14. new Thread(() -> {
  15. for(int i=0;i<EVENT_COUNT;i++) {
  16. log.info("publich {}", i);
  17. multiProducerService.publish(String.valueOf(i));
  18. }
  19. }).start();
  20. // 再启动一个线程,用第二个生产者生产事件
  21. new Thread(() -> {
  22. for(int i=0;i<EVENT_COUNT;i++) {
  23. log.info("publishWithProducer2 {}", i);
  24. try {
  25. multiProducerService.publishWithProducer2(String.valueOf(i));
  26. } catch (Exception e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. }).start();
  31. // 当前线程开始等待,前面的service.setCountDown方法已经告诉过service,
  32. // 等消费到expectEventCount个消息时,就执行countDownLatch.countDown方法
  33. // 千万注意,要调用await方法,而不是wait方法!
  34. countDownLatch.await();
  35. // 消费的事件总数应该等于发布的事件数
  36. assertEquals(expectEventCount, multiProducerService.eventCount());
  37. }
  • 测试结果如下,测试通过,符合预期:

C1、C2独立消费,C3依赖C1和C2

  • 逻辑图如下:

  • 实现代码如下,非常简单,依赖关系用then即可实现:
  1. package com.bolingcavalry.service.impl;
  2. import com.bolingcavalry.service.ConsumeModeService;
  3. import com.bolingcavalry.service.MailEventHandler;
  4. import com.bolingcavalry.service.MailWorkHandler;
  5. import com.bolingcavalry.service.SmsEventHandler;
  6. import org.springframework.stereotype.Service;
  7. @Service("scene5")
  8. public class Scene5 extends ConsumeModeService {
  9. @Override
  10. protected void disruptorOperate() {
  11. MailEventHandler c1 = new MailEventHandler(eventCountPrinter);
  12. MailEventHandler c2 = new MailEventHandler(eventCountPrinter);
  13. MailEventHandler c3 = new MailEventHandler(eventCountPrinter);
  14. disruptor
  15. // C1、C2独立消费
  16. .handleEventsWith(c1, c2)
  17. // C3依赖C1和C2
  18. .then(c3);
  19. }
  20. }
  • 单元测试代码:
  1. @Autowired
  2. @Qualifier("scene5")
  3. Scene5 scene5;
  4. @Test
  5. public void testScene5 () throws InterruptedException {
  6. log.info("start testScene5");
  7. testConsumeModeService(scene5,
  8. EVENT_COUNT,
  9. // 三个独立消费者,一共消费300个事件
  10. EVENT_COUNT * 3);
  11. }
  • 为了节省篇幅,测试结果就不贴了,要注意的是,每个事件都一定是C1和C2先消费过,才会被C3消费到;

C1独立消费,C2和C3也独立消费,但依赖C1,C4依赖C2和C3

  • 逻辑图如下:

  • 实现代码如下:
  1. package com.bolingcavalry.service.impl;
  2. import com.bolingcavalry.service.ConsumeModeService;
  3. import com.bolingcavalry.service.MailEventHandler;
  4. import org.springframework.stereotype.Service;
  5. @Service("scene6")
  6. public class Scene6 extends ConsumeModeService {
  7. @Override
  8. protected void disruptorOperate() {
  9. MailEventHandler c1 = new MailEventHandler(eventCountPrinter);
  10. MailEventHandler c2 = new MailEventHandler(eventCountPrinter);
  11. MailEventHandler c3 = new MailEventHandler(eventCountPrinter);
  12. MailEventHandler c4 = new MailEventHandler(eventCountPrinter);
  13. disruptor
  14. // C1
  15. .handleEventsWith(c1)
  16. // C2和C3也独立消费
  17. .then(c2, c3)
  18. // C4依赖C2和C3
  19. .then(c4);
  20. }
  21. }
  • 单元测试代码:
  1. @Autowired
  2. @Qualifier("scene6")
  3. Scene6 scene6;
  4. @Test
  5. public void testScene6 () throws InterruptedException {
  6. log.info("start testScene6");
  7. testConsumeModeService(scene6,
  8. EVENT_COUNT,
  9. // 四个独立消费者,一共消费400个事件
  10. EVENT_COUNT * 4);
  11. }

C1和C2独立消费,C3和C4也是独立消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4

  • 逻辑图如下:

  • 实现代码如下:
  1. package com.bolingcavalry.service.impl;
  2. import com.bolingcavalry.service.ConsumeModeService;
  3. import com.bolingcavalry.service.MailEventHandler;
  4. import org.springframework.stereotype.Service;
  5. @Service("scene7")
  6. public class Scene7 extends ConsumeModeService {
  7. @Override
  8. protected void disruptorOperate() {
  9. MailEventHandler c1 = new MailEventHandler(eventCountPrinter);
  10. MailEventHandler c2 = new MailEventHandler(eventCountPrinter);
  11. MailEventHandler c3 = new MailEventHandler(eventCountPrinter);
  12. MailEventHandler c4 = new MailEventHandler(eventCountPrinter);
  13. MailEventHandler c5 = new MailEventHandler(eventCountPrinter);
  14. disruptor
  15. // C1和C2独立消费
  16. .handleEventsWith(c1, c2)
  17. // C3和C4也是独立消费,但C3和C4都依赖C1和C2
  18. .then(c3, c4)
  19. // 然后C5依赖C3和C4
  20. .then(c5);
  21. }
  22. }
  • 单元测试代码:
  1. @Autowired
  2. @Qualifier("scene7")
  3. Scene7 scene7;
  4. @Test
  5. public void testScene7 () throws InterruptedException {
  6. log.info("start testScene7");
  7. testConsumeModeService(scene7,
  8. EVENT_COUNT,
  9. // 五个独立消费者,一共消费500个事件
  10. EVENT_COUNT * 5);
  11. }

C1和C2共同消费,C3和C4也是共同消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4

  • 逻辑图如下:

  • 实现代码如下:
  1. package com.bolingcavalry.service.impl;
  2. import com.bolingcavalry.service.ConsumeModeService;
  3. import com.bolingcavalry.service.MailEventHandler;
  4. import com.bolingcavalry.service.MailWorkHandler;
  5. import org.springframework.stereotype.Service;
  6. /**
  7. * @author will (zq2599@gmail.com)
  8. * @version 1.0
  9. * @description: C1和C2共同消费,C3和C4也是共同消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4
  10. * @date 2021/5/23 11:05
  11. */
  12. @Service("scene8")
  13. public class Scene8 extends ConsumeModeService {
  14. @Override
  15. protected void disruptorOperate() {
  16. MailWorkHandler c1 = new MailWorkHandler(eventCountPrinter);
  17. MailWorkHandler c2 = new MailWorkHandler(eventCountPrinter);
  18. MailWorkHandler c3 = new MailWorkHandler(eventCountPrinter);
  19. MailWorkHandler c4 = new MailWorkHandler(eventCountPrinter);
  20. MailWorkHandler c5 = new MailWorkHandler(eventCountPrinter);
  21. disruptor
  22. // C1和C2共同消费
  23. .handleEventsWithWorkerPool(c1, c2)
  24. // C3和C4也是独立消费,但C3和C4都依赖C1和C2
  25. .thenHandleEventsWithWorkerPool(c3, c4)
  26. // 然后C5依赖C3和C4
  27. .thenHandleEventsWithWorkerPool(c5);
  28. }
  29. }
  • 单元测试代码:
  1. @Autowired
  2. @Qualifier("scene8")
  3. Scene8 scene8;
  4. @Test
  5. public void testScene8 () throws InterruptedException {
  6. log.info("start testScene8");
  7. testConsumeModeService(scene8,
  8. EVENT_COUNT,
  9. // C1和C2共同消费,C3和C4共同消费,C5虽然只是一个,但也是共同消费模式,
  10. // 也就是一共有三组消费者,所以一共消费300个事件
  11. EVENT_COUNT * 3);
  12. }

C1和C2共同消费,C3和C4独立消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4

  • 逻辑图如下:

  • 实现代码如下:
  1. package com.bolingcavalry.service.impl;
  2. import com.bolingcavalry.service.ConsumeModeService;
  3. import com.bolingcavalry.service.MailEventHandler;
  4. import com.bolingcavalry.service.MailWorkHandler;
  5. import org.springframework.stereotype.Service;
  6. @Service("scene9")
  7. public class Scene9 extends ConsumeModeService {
  8. @Override
  9. protected void disruptorOperate() {
  10. MailWorkHandler c1 = new MailWorkHandler(eventCountPrinter);
  11. MailWorkHandler c2 = new MailWorkHandler(eventCountPrinter);
  12. MailEventHandler c3 = new MailEventHandler(eventCountPrinter);
  13. MailEventHandler c4 = new MailEventHandler(eventCountPrinter);
  14. MailEventHandler c5 = new MailEventHandler(eventCountPrinter);
  15. disruptor
  16. // C1和C2共同消费
  17. .handleEventsWithWorkerPool(c1, c2)
  18. // C3和C4独立消费,但C3和C4都依赖C1和C2
  19. .then(c3, c4)
  20. // 然后C5依赖C3和C4
  21. .then(c5);
  22. }
  23. }
  • 单元测试代码:
  1. @Autowired
  2. @Qualifier("scene9")
  3. Scene9 scene9;
  4. @Test
  5. public void testScene9 () throws InterruptedException {
  6. log.info("start testScene9");
  7. testConsumeModeService(scene9,
  8. EVENT_COUNT,
  9. // C1和C2共同消费(100个事件),
  10. // C3和C4独立消费(200个事件),
  11. // C5独立消费(100个事件),
  12. // 所以一共消费400个事件
  13. EVENT_COUNT * 4);
  14. }

C1和C2独立消费,C3和C4是共同消费,但C3和C4都依赖C1和C2,然后C5依赖C3和C4

  • 逻辑图如下:

  • 实现代码如下:
  1. package com.bolingcavalry.service.impl;
  2. import com.bolingcavalry.service.ConsumeModeService;
  3. import com.bolingcavalry.service.MailEventHandler;
  4. import com.bolingcavalry.service.MailWorkHandler;
  5. import org.springframework.stereotype.Service;
  6. @Service("scene10")
  7. public class Scene10 extends ConsumeModeService {
  8. @Override
  9. protected void disruptorOperate() {
  10. MailEventHandler c1 = new MailEventHandler(eventCountPrinter);
  11. MailEventHandler c2 = new MailEventHandler(eventCountPrinter);
  12. MailWorkHandler c3 = new MailWorkHandler(eventCountPrinter);
  13. MailWorkHandler c4 = new MailWorkHandler(eventCountPrinter);
  14. MailEventHandler c5 = new MailEventHandler(eventCountPrinter);
  15. disruptor
  16. // C1和C2共同消费
  17. .handleEventsWith(c1, c2)
  18. // C3和C4是共同消费,但C3和C4都依赖C1和C2
  19. .thenHandleEventsWithWorkerPool(c3, c4)
  20. // 然后C5依赖C3和C4
  21. .then(c5);
  22. }
  23. }
  • 单元测试代码:
  1. @Test
  2. public void testScene10 () throws InterruptedException {
  3. log.info("start testScene10");
  4. testConsumeModeService(scene10,
  5. EVENT_COUNT,
  6. // C1和C2独立消费(200个事件),
  7. // C3和C4共同消费(100个事件),
  8. // C5独立消费(100个事件),
  9. // 所以一共消费400个事件
  10. EVENT_COUNT * 4);
  11. }
  • 至此,一些常见场景的代码已完成,希望本文能给您一些参考,帮您更得心应手的用好这个优秀的工具;

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

欢迎关注公众号:程序员欣宸

微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...

https://github.com/zq2599/blog_demos

disruptor笔记之六:常见场景的更多相关文章

  1. disruptor笔记之二:Disruptor类分析

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. disruptor笔记之三:环形队列的基础操作(不用Disruptor类)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. disruptor笔记之四:事件消费知识点小结

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. disruptor笔记之五:事件消费实战

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. disruptor笔记之七:等待策略

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. disruptor笔记之八:知识点补充(终篇)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  7. disruptor笔记之一:快速入门

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  8. 【Visual C++】游戏编程学习笔记之六:多背景循环动画

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44264153 作者:ZeeCod ...

  9. [oracle]TX行锁发生的常见场景(转贴)

    TX行锁发生的常见场景: 1.当前会话要更新或删除的记录,已经被其他会话更新或删除. 2.对于表上有唯一索引的情况,多个会话插入或更新为相同的键值. 3.对于表上有位图索引的情况,多个会话即使更新不同 ...

随机推荐

  1. 服务端负载监控-参考srs服务器源码

    #include <map> #include <stdio.h> using namespace std; struct SrsMemoryObject { void* pt ...

  2. 【java虚拟机】Java内存模型

    作者:平凡希 原文地址:https://www.cnblogs.com/xiaoxi/p/7518259.html 一.什么是Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Jav ...

  3. Sublime Text 快速分别独立选中多行

    效果图 直接上代码 import sublime, sublime_plugin # 独立选择每一行(在当前选中范围内) class SelectEverySingleLine(sublime_plu ...

  4. SpringBoot2.x+mybatis plus3.x集成Activit7版本

    最近在写一个开源项目ruoyi-vue-pro,暂时负责Activiti7工作流的搭建,接这个任务一个原因,是比较好奇Activiti7版本与先前的5.6版本究竟有什么区别,因为先前在工作当中,最开始 ...

  5. Vue 2.0 与 Vue 3.0 响应式原理比较

    Vue 2.0 的响应式是基于Object.defineProperty实现的 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 prop ...

  6. noip模拟42

    A. 卷 发现乘积足以爆 \(long\) \(long\),但是数据随机,可以略忽略精度问题 一个快速降低数的级别的方法是取对数,由于有性质 \(log(x * y)=logx+logy\),合并时 ...

  7. MongoDB(4)- Collection 集合相关

    Collection MongoDB 将文档存储在集合中 集合存储在 Database 中 集合类似于关系数据库(Mysql)中的表 如果集合不存在,则 MongoDB 会在第一次存储该集合数据时创建 ...

  8. java 多线程Thread和Runnable的区别

    如果一个类继承Thread,则不适合资源共享.但是如果实现了Runable接口的话,则很容易的实现资源共享 实现Runnable接口比继承Thread类所具有的优势:1. 适合多个相同的程序代码的线程 ...

  9. IPsec NAT-T说明和环境搭建

    1. IPsec与NAT的关系 NAT作为一个IPV4的地址转换协议,它最初的目的是用来最解决IPv4地址不足的问题.通过NAT协议,局域网内的多个主机可以共同使用一个公网地址,这在很大程度上减轻了I ...

  10. SQL-DELETE触发器练习

    &练习一 如下所示三张表( student,grade,student_updata_before ): student表 grade表 Student_update_before表 # 触发 ...