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. 重新学习之spring第一个程序,配置IOC容器

    第一步:导入相关jar包(此范例导入的是spring3.2.4版本,spring2.5版本只需要导入spring核心包即可) 第二步:在项目的src下配置applicationContext.xml的 ...

  2. Mac: iTerm2使用

    From: http://www.cnblogs.com/noTice520/p/3190529.html 之前一直有朋友要我分享下在用的mac软件,今天有空就来写一下,可能不止于软件,会有一些配置或 ...

  3. harbor rest api 转graphql api

    原理 实际上就是使用graphql 中的binding,首先基于swagger api 进行schema 生成,后边就是 使用binding 进行graphql 请求api 转换为rest api 请 ...

  4. Install LAMP Server (Apache, MariaDB, PHP) On CentOS/RHEL/Scientific Linux 7

    Install LAMP Server (Apache, MariaDB, PHP) On CentOS/RHEL/Scientific Linux 7 By SK  - August 12, 201 ...

  5. CCFlow SDK模式开发(有比较详细的代码,以服务的形式与ccflow数据库进行数据交互)

    http://www.cnblogs.com/s0611163/p/3963142.html 需求: 1.业务数据要保存在我们自己的数据库里     2.CCFlow有保存草稿的功能,但是领导要求每个 ...

  6. Jmeter监控服务器的CPU

    1.Jmeter监控服务器的软件:链接: https://pan.baidu.com/s/1Ag75IXqR2XMe8RpZA4nvHg 密码: yki2 2.(1)Window版本 <1> ...

  7. Ubuntu14.04安装有道词典(openyoudao)

    1. Openyoudao介绍 Openyoudao是有道字典在linux下的客户端,在取词翻译的基础上,对查询到的信息进行有效的整合.目前已经发布了0.4版本,新增了google翻译功能,可提供72 ...

  8. python调用R语言,关联规则可视化

    首先当然要配置r语言环境变量什么的 D:\R-3.5.1\bin\x64; D:\R-3.5.1\bin\x64\R.dll;D:\R-3.5.1;D:\ProgramData\Anaconda3\L ...

  9. python3调用阿里云短信服务

    #!/usr/bin/env python#-*- coding:utf-8 -*-#Author:lzd import uuidimport datetimeimport hmacimport ba ...

  10. Calendar时间操作

    1.Calendar是Java提供的一个util类,在java.util中.我们在开发过程中经常遇到,需要对时间进行处理的方式.而Java中Calendar给我们提供了很好的控制方式. 2.我这里主要 ...