Disruptor 系列(二)使用场景

今天用一个订单问题来加深对 Disruptor 的理解。当系统中有订单产生时,系统首先会记录订单信息。同时也会发送消息到其他系统处理相关业务,最后才是订单的处理。

代码包含以下内容:

1) 事件对象 Event

2)三个消费者 Handler

3)一个生产者 Producer

4)执行 Main 方法

一、订单处理系统代码

(1) Event

public class Trade {  

    private String id;//ID
private String name;
private double price;//金额
private AtomicInteger count = new AtomicInteger(0); // 省略getter/setter
}

(2) Handler 类

一个负责存储订单信息,一个负责发送 kafka 信息到其他系统中,最后一个负责处理订单信息。

import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.WorkHandler; /**
* 第一个 Handler1,存储到数据库中
*/
public class Handler1 implements EventHandler<Trade>, WorkHandler<Trade> { @Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
this.onEvent(event);
} @Override
public void onEvent(Trade event) throws Exception {
long threadId = Thread.currentThread().getId(); // 获取当前线程id
String id = event.getId(); // 获取订单号
System.out.println(String.format("%s:Thread Id %s 订单信息保存 %s 到数据库中 ....",
this.getClass().getSimpleName(), threadId, id));
}
}
import com.lmax.disruptor.EventHandler;

/**
* 第二个 Handler2,订单信息发送到其它系统中
*/
public class Handler2 implements EventHandler<Trade> { @Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
long threadId = Thread.currentThread().getId(); // 获取当前线程id
String id = event.getId(); // 获取订单号
System.out.println(String.format("%s:Thread Id %s 订单信息 %s 发送到 karaf 系统中 ....",
this.getClass().getSimpleName(), threadId, id));
}
}
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.WorkHandler; /**
* 第三个 Handler2,处理订单信息
*/
public class Handler3 implements EventHandler<Trade>, WorkHandler<Trade> { @Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
onEvent(event);
} @Override
public void onEvent(Trade event) throws Exception {
long threadId = Thread.currentThread().getId(); // 获取当前线程id
String id = event.getId(); // 获取订单号
System.out.println(String.format("%s:Thread Id %s 订单信息 %s 处理中 ....",
this.getClass().getSimpleName(), threadId, id));
}
}

(3) Producer 类

import com.lmax.disruptor.EventTranslator;
import com.lmax.disruptor.dsl.Disruptor; import java.util.UUID;
import java.util.concurrent.CountDownLatch; public class TradePublisher implements Runnable { Disruptor<Trade> disruptor;
private CountDownLatch latch; private static int LOOP = 1; // 模拟百万次交易的发生 public TradePublisher(CountDownLatch latch, Disruptor<Trade> disruptor) {
this.disruptor=disruptor;
this.latch=latch;
} @Override
public void run() {
TradeEventTranslator tradeTransloator = new TradeEventTranslator();
for(int i = 0; i < LOOP; i++) {
disruptor.publishEvent(tradeTransloator);
}
latch.countDown();
} } class TradeEventTranslator implements EventTranslator<Trade>{ @Override
public void translateTo(Trade event, long sequence) {
event.setId(UUID.randomUUID().toString());
} }

(4) 执行的 Main 方法

package com.github.binarylei.disruptor.demo3;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import com.lmax.disruptor.BusySpinWaitStrategy;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.EventHandlerGroup;
import com.lmax.disruptor.dsl.ProducerType; public class Main {
public static void main(String[] args) throws InterruptedException { long beginTime=System.currentTimeMillis();
int bufferSize=1024;
ExecutorService executor=Executors.newFixedThreadPool(8); Disruptor<Trade> disruptor = new Disruptor<>(new EventFactory<Trade>() {
@Override
public Trade newInstance() {
return new Trade();
}
}, bufferSize, executor, ProducerType.SINGLE, new BusySpinWaitStrategy()); //菱形操作
//使用disruptor创建消费者组C1,C2
EventHandlerGroup<Trade> handlerGroup =
disruptor.handleEventsWith(new Handler1(), new Handler2());
//声明在C1,C2完事之后执行JMS消息发送操作 也就是流程走到C3
handlerGroup.then(new Handler3()); disruptor.start();//启动
CountDownLatch latch=new CountDownLatch(1);
//生产者准备
executor.submit(new TradePublisher(latch, disruptor)); latch.await();//等待生产者完事. disruptor.shutdown();
executor.shutdown();
System.out.println("总耗时:"+(System.currentTimeMillis()-beginTime));
}
}

测试结果如下:

Handler1:Thread Id 10 订单信息保存 a097c77d-08f1-430a-8342-2143963f268f 到数据库中 ....
Handler2:Thread Id 11 订单信息 a097c77d-08f1-430a-8342-2143963f268f 发送到 karaf 系统中 ....
Handler3:Thread Id 13 订单信息 a097c77d-08f1-430a-8342-2143963f268f 处理中 ....
总耗时:1631

可以看到 Handler3 在 Handler1 和 Handler2 执行完成后才执行。

二、Disruptor DSL

虽然 disruptor 模式使用起来很简单,但是建立多个消费者以及它们之间的依赖关系需要的样板代码太多了。为了能快速又简单适用于99%的场景,我为 Disruptor 模式准备了一个简单的领域特定语言(DSL),定义了消费顺序。更多Disruptor场景使用

在讲解 Disruptor DSL 之前先看一下多个消费者不重复消费的问题。

2.1 多个消费者不重复消费

默认一个消费者一个线程,如果想要实现 C3 多个消费者共同不重复消费数据,可以使用 handlerGroup.thenHandleEventsWithWorkerPool(customers)

//使用disruptor创建消费者组C1, C2
EventHandlerGroup<Trade> handlerGroup = disruptor.handleEventsWith(new Handler1(), new Handler2()); // 多个消费者不重复消费
Handler3[] customers = new Handler3[]{new Handler3(), new Handler3(), new Handler3()};
handlerGroup.thenHandleEventsWithWorkerPool(customers);

2.2 消费者的“四边形模式”

在这种情况下,只要生产者(P1)将元素放到ring buffer上,消费者C1和C2就可以并行处理这些元素。但是消费者C3必须一直等到C1和C2处理完之后,才可以处理。在现实世界中的对应的案例就像:在处理实际的业务逻辑(C3)之前,需要校验数据(C1),以及将数据写入磁盘(C2)。

//1. 使用disruptor创建消费者组C1,C2
EventHandlerGroup<Trade> handlerGroup = disruptor.handleEventsWith(new Handler1(), new Handler2()); //2. 声明在C1,C2完事之后执行JMS消息发送操作 也就是流程走到C3
handlerGroup.then(new Handler3());

2.3 消费者的“顺序执行模式”

disruptor.handleEventsWith(new Handler1()).
handleEventsWith(new Handler2()).
handleEventsWith(new Handler3());

2.4 消费者的“六边形模式”

我们甚至可以在一个更复杂的六边形模式中构建一个并行消费者链:

Handler1 h1 = new Handler1();
Handler2 h2 = new Handler2();
Handler3 h3 = new Handler3();
Handler4 h4 = new Handler4();
Handler5 h5 = new Handler5();
disruptor.handleEventsWith(h1, h2);
disruptor.after(h1).handleEventsWith(h4);
disruptor.after(h2).handleEventsWith(h5);
disruptor.after(h4, h5).handleEventsWith(h3);

每天用心记录一点点。内容也许不重要,但习惯很重要!

Disruptor 系列(二)使用场景的更多相关文章

  1. Disruptor 系列(一)快速入门

    Disruptor 系列(一)快速入门 Disruptor:是一个开源的并发框架,能够在 无锁 的情况下实现网络的 Queue 并发操作,所以处理数据的能力比 Java 本身提供的并发类容器要大的多, ...

  2. Web 前端开发人员和设计师必读文章推荐【系列二十八】

    <Web 前端开发精华文章推荐>2014年第7期(总第28期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  3. WPF入门教程系列(二) 深入剖析WPF Binding的使用方法

    WPF入门教程系列(二) 深入剖析WPF Binding的使用方法 同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProp ...

  4. highcharts 结合phantomjs纯后台生成图片系列二之php2

    上篇文章中介绍了phantomjs的使用场景,方法. 本篇文章详细介绍使用php,highcharts 结合phantomjs纯后台生成图片.包含一步步详细的php代码 一.highcharts 结合 ...

  5. highcharts 结合phantomjs纯后台生成图片系列二之php

    上篇文章中介绍了phantomjs的使用场景,方法.本篇文章详细介绍使用php,highcharts 结合phantomjs纯后台生成图片. 一.准备: 下载phantomjs解析插件,从 highc ...

  6. MySQL并发复制系列二:多线程复制

     http://blog.itpub.net/28218939/viewspace-1975822/ 并发复制(Parallel Replication) 系列二: Enhanced Multi-th ...

  7. MySQL并发复制系列二:多线程复制 2016

    并发复制(Parallel Replication) 系列二: Enhanced Multi-threaded Slaves作者:沃趣科技MySQL数据库工程师  麻鹏飞 首先梳理下传统MySQL/M ...

  8. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  9. mybatis入门系列二之输入与输出参数

    mybatis入门系列二之详解输入与输出参数   基础知识   mybatis规定mapp.xml中每一个SQL语句形式上只能有一个@parameterType和一个@resultType 1. 返回 ...

随机推荐

  1. MAC远程桌面连接问题

    如图,始终提示“证书或相关链无效.” 点击“取消” 点击“编辑连接...” 点击“打开” 点击“安全性” 选择“即使验证失败,也始终连接” OK

  2. SqlServer高级特性--存储过程

    需求: 用户需要提交加密数据,提交之后需要解密还原,还原有两种结果:成功和失败! 100个用户,之前7天,判断是否有提交数据,如果有提交有数据,判断是否解密成功(分别存在两个表中).如果没有提交,显示 ...

  3. 捕捉过滤器(CaptureFilters)和显示过滤器(DisplayFilters)--Wireshark

    Wireshark的基本使用——过滤器 前言 网络上关于Wireshark的教程已有不少,博主就简单介绍一下Wireshark分析数据包时最重要的技巧之一的过滤器..一次性嗅探到的数据包有很多,想要高 ...

  4. 浅谈c#垃圾回收机制(GC)

    写了一个window服务,循环更新sqlite记录,内存一点点稳步增长.三天后,内存溢出.于是,我从自己的代码入手,查找到底哪儿占用内存释放不掉,最终明确是调用servicestack.ormlite ...

  5. test20181025 Color

    题意 分析 自己的想法 可以莫队+平衡树. 对每个颜色维护一颗平衡树,然后移动莫队端点的时候在平衡树中查询. 区间加操作容易实现. 单点修改转化为平衡树的插入删除. 感谢Z前辈的指导. 时间复杂度\( ...

  6. timescaledb 几个方便的api

    timescaledb 提供了内置的api 操作,方便我们进行操作控制 hypertable 控制api add_dimension 向hypertable添加一个额外的分片方式,可以做为分片列有时间 ...

  7. Linux配置Nginx+Tomcat负载均衡

    cd /usr/local/tomcat1/webapps/ROOT/ tar -zxvf nginx-1.14.2.tar.gz -C /usr/local 一.Linux配置Nginx 一.下载N ...

  8. 随笔:关于 FastAdmin ueditor 插件 中的 rand mt_rand mt_getrandmax 问题

    随笔:关于 FastAdmin ueditor 插件 中的 rand mt_rand mt_getrandmax 问题 问题来源 一位小伙伴在使用 Ueditor 插件时出错,因为用的是 php7.1 ...

  9. Jenkins 基础入门

    原文地址:Jenkins 基础入门 博客地址:http://www.extlight.com 一.前言 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作, ...

  10. MySQL 多表关联更新及删除

    目录: <MySQL中的两种临时表> <MySQL 多表关联更新及删除> <mysql查询优化之三:查询优化器提示(hint)> 一.      多表关联更新 问题 ...