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. TCP滑动窗口与回退N针协议

    [转]TCP 滑动窗口协议/1比特滑动窗口协议/后退n协议/选择重传协议 2014-1-5阅读884 评论0 本文转自 http://www.cnblogs.com/ulihj/archive/201 ...

  2. 【Beanstalkd】Beanstalkd消息队列的安装与使用

    一.Beanstalkd是什么? Beanstalkd是一个高性能,轻量级的分布式内存队列 二.Beanstalkd特性 1.支持优先级(支持任务插队)2.延迟(实现定时任务)3.持久化(定时把内存中 ...

  3. 基于Linux C的socket抓包程序和Package分析 (一)

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/guankle/article/details/27538031  測试执行平台:CentOS 6 ...

  4. 深入理解Java虚拟机,intern

    1,在java1.7下面,intern不再复制实例,只存第一个引用,也就是new出来的有可能和intern相同(第一次情况 2,平时的new已经暗含了一个常量池,所有不适合上面情况, 参考:https ...

  5. 获取刚刚插入表格的这条信息的自增ID

    获取刚刚插入表格的这条信息的自增ID var conn=getConnection(); var msql="INSERT INTO " + table +" (&quo ...

  6. Zabbix 报警通知邮件和微信vim /etc/hosts

    1安装 sendmail # yum -y install sendmail echo 'This is test mail'>body.txt mail -s 'Test mail' 3013 ...

  7. JS 奇淫巧技 转

    1. 在 String.prototype.replace 方法中使用 /g 和 /i 标志位 令很多 JavaScript 初学者意外的是,字符串的 replace 方法并不会 替换所有匹配的子串— ...

  8. CentOS6.5 安装codeblocks-13.12

    安装环境CentOS6.5 启动命令行 1.先安装gcc和gcc++,这个可以直接安装 # yum install gcc # yum install gcc-c++ 2.再安装gtk2,也是直接安装 ...

  9. pycharm -- 小技巧1 (显示文件的代码结构以及错误提示)

    背景介绍 今天上午,在调用同事昨天给的算法程序时出了点问题,于是请同事来我这边一起调代码.大致场景描述如下: 我:B神,你昨天下班前给我的那个算法程序我这边调用的时候出现错误啦,请你过来看下呗. 同事 ...

  10. 管理Linux服务器的用户和组

    管理Linux服务器的用户和组 Linux操作系统是一个多用户多任务的操作系统,允许多个用户同时登录到系统,使用系统资源. 为了使所有用户的工作顺利进行,保护每个用户的文件和进程,规范每个用户的权限, ...