文档章节

不一样的事件总线 - NextEvents

陈哈哈GO
 陈哈哈GO
发布于 2016/04/11 10:46
字数 1412
阅读 29
收藏 0

Why NextEvents

总会有人说Guava.EventBus和Otto,还有Greenbot.EventBus已经非常优秀,你再造轮子有什么鬼用?

因为他们是圆轮子,我想要方的。他们没有,我就自己造啦!

Greenbot.EventBus、Guava.EventBus和Otto类似,对@Subscribe注解的方法进行回调,并根据方法参数类型来过滤。NextEvents 基于它们的设计思想,并且在回调调度粒度、过滤方式等做了改进,适应项目的需要。

在此前Android项目实践中使用到Otto,它非常好地解耦一些模块之间的弱关联。但过程中,我发现Otto未能解决遇到的问题:

  • 我需要在@Subscribe方法中自行决定回调内部的执行线程,而不是由调用者或总线本身决定。可以在@Subscriber注解中声明调度方式是最好的。我在其它相似的库中也看到同样的实现,看来大家的思想都是一致的。

  • 项目中多个不同逻辑的回调方法,其参数是相同的。Otto基于类型过滤,使得我只能在回调之后再过滤一次。我需要在定义方法时就决定它只接受哪一类型的事件。使用消息名来标识和过滤,与传统Web后端的消息总线相同。

  • 触发执行一个方法,前提是多个任务的处理结果。(这是未来版本多事件触发的特性,现在还没实现)

已支持特性

  • 支持使用 <EventHandler> 接口回调;
  • 支持使用 @Subscribe 注解方法回调;
  • 支持多种回调方式: CALLER_THREAD / IO_THREAD / MAIN_THREAD (For Android only)
  • 支持自定义回调目标的调度处理 <Scheduler>
  • 支持事件组

未来支持特性

  • 多事件
  • 自定义事件匹配处理
  • 事件转换

触发条件

在 <EventHandler> 接口回调模式下,所有 <EventFilter> 返回 True 即触发回调。

在 @Subscribe 注解模式下,触发回调方法需要满足两个条件:

  1. 定义的事件名与发送事件名相同;
  2. 定义的参数与发送事件负载,数量相同且类型相同;

对于条件2,以下两个特殊情况也满足:

  • 定义的参数数量为0;
  • 定义唯一参数且类型为 Any;
// 当发生"str-event"事件,并且事件类型为String时,将被回调;
@Subscribe(events = "str-event")
void handleWithArgEvents(String event) {
    ...
}

// 定义参数数量为0: 当发生"str-event"事件时,无论事件负载是什么类型,都被回调。
@Subscribe(events = "str-event")
void handleNoArgEvent( ) {
    ...
}

// 定义唯一参数且类型为Any: 当发生"str-event"事件时,事件负载为任意数量、任意类型,都被回调。
@Subscribe(events = "str-event")
void handleAnyTypeEvent(Any events) {
    Object[] payloadValues = events.values;
    Class[] payloadTypes = events.types;
    ...
}

// 发射 str-event 事件,上面定义的全部方法都触发回调
nextEvents.emit("str-event", new String("this is an event payload"))

多负载事件

有些事件可能有多个负载,NextEvents可以直接提交多个负载,无须为这些负载创建包装类。


// 当发生事件时,会将参数按参数类型顺序依次填充
@Subscribe(events = "profile-event")
void handleEvent(String name, int age, float weight) {
    ...
}

// 发射事件,回调方法的参数将会按参数类型顺序依次填充这些数值
nextEvents.emit("profile-event", "yoojia", 18, 1024.0f)

多负载参数顺序

NextEvents对多负载事件支持乱序参数。在符合触发条件的前提下,上面代码为例,以下的方法都会触发回调:


@Subscribe(events = "profile-event")
void handleEvent1(int age, float weight, String name) {
    ...
}

@Subscribe(events = "profile-event")
void handleEvent2(float weight, int age, String name) {
    ...
}

乱序符合以下规则:

  • 各个参数类型互不相同时,可以是任意顺序。
  • 存在相同参数类型的,填充顺序按 emit() 中的参数顺序。如:
@Subscribe(events = "same-type-event")
void handleEvent(int height, int age, String name) {
    // height == 1024 --> TRUE
    // age == 18 --> TRUE
    ...
}

@Subscribe(events = "same-type-event")
void handleEvent(int age, int height, String name) {
    // age == 1024 --> TRUE
    // height == 18 --> TRUE
    ...
}

nextEvents.emit("same-type-event", 1024, 18, "yoojia")

Dead event

如果发送事件没有回调目标方法或者Handler时,原事件将被包装成DeadEvent重新发送。可以通过订阅EventPayload.DEAD_EVENT事件来处理DeadEvent。

DeadEvent事件与普通事件一样,唯一区别是它的事件名固定为EventPayload.DEAD_EVENT的值。

@Subscribe(events = EventPayload.DEAD_EVENT)
void onMissedTyped(String evt) {
    ...
}

Benchmark

v2.0OS X 10.11.3' / 2.6 GHz Intel Core i5 / 8 GB / Java(TM) SE Runtime Environment (build 1.8.0_60-b27)环境下的性能对比情况如下表:

测试类型/负载方式TPS/QPS总投递时间总运行时间投递事件量
MultiThreads(1ms Payload)274947ms727ms2000
SharedThreads(1ms Payload)30128ms663ms2000
CallerThread(1ms Payload)7392705ms2705ms2000
GuavaEvents(1ms Payload)7172785ms2785ms2000
MultiThreads(Nop Payload)19475821026ms1026ms2000000
SharedThreads(Nop Payload)8912572243ms2244ms2000000
CallerThread(Nop Payload)3933523508ms508ms2000000
GuavaEvents(Nop Payload)2083789959ms959ms2000000

v2.0Ubuntu 14.04 LTS / 3.6 GHz AMD A8-5600K / 8 GB / Java(TM) SE Runtime Environment (build 1.8.0_65-b17) 环境下的性能对比情况如下:

测试类型/负载方式TPS/QPS总投递时间总运行时间投递事件量
MultiThreads(1ms Payload)324222ms616ms2000
SharedThreads(1ms Payload)33513ms596ms2000
CallerThread(1ms Payload)9142187ms2187ms2000
GuavaEvents(1ms Payload)8902245ms2245ms2000
MultiThreads(Nop Payload)10785251846ms1854ms2000000
SharedThreads(Nop Payload)13735401427ms1456ms2000000
CallerThread(Nop Payload)3834583521ms521ms2000000
GuavaEvents(Nop Payload)15785181266ms1267ms2000000

v2.0Windows 10 64x / 3.2 GHz Intel i5-4460 / 8 GB / Java(TM) SE Runtime Environment (build 1.8.0_74-b02) 环境下的性能对比情况如下:

测试类型/负载方式TPS/QPS总投递时间总运行时间投递事件量
MultiThreads(1ms Payload)344710ms580ms2000
SharedThreads(1ms Payload)33761ms592ms2000
CallerThread(1ms Payload)9022215ms2215ms2000
GuavaEvents(1ms Payload)8842260ms2260ms2000
MultiThreads(Nop Payload)19355081033ms1033ms2000000
SharedThreads(Nop Payload)2261975882ms884ms2000000
CallerThread(Nop Payload)4602303434ms434ms2000000
GuavaEvents(Nop Payload)3980131502ms502ms2000000

© 著作权归作者所有

共有 人打赏支持
陈哈哈GO
粉丝 18
博文 6
码字总数 10904
作品 3
深圳
高级程序员
私信 提问
[Abp 源码分析]九、事件总线

0.简介 事件总线就是订阅/发布模式的一种实现,本质上事件总线的存在是为了降低耦合而存在的。 从上图可以看到事件由发布者发布到事件总线处理器当中,然后经由事件总线处理器调用订阅者的处...

myzony
2018/08/03
0
0
Linux总线设备驱动框架的理解(非常棒的文章!)

以下内容源于微信公众号:嵌入式企鹅圈。有格式内容上的修改,如有侵权,请告知删除。 Linux的设备驱动框架,即某类设备对应的驱动的框架。 这里是“Linux总线设备驱动框架”,应该这样理解,...

oqqHuTu12345678
2017/12/29
0
0
从需求的角度去理解Linux系列:总线、设备和驱动

《从需求的角度去理解Linux系列:总线、设备和驱动》是一篇有关如何学习嵌入式Linux系统的方法论文章,也是从需求的角度去理解Linux系统软件的开篇。这是作者精心撰写的经验总结,希望嵌入式...

yueqian_scut
2015/09/23
0
0
MSI cache一致性协议

按照高速缓存的写策略的不同,有写直达和写回WB两种高速缓存: 1. 写直达高速缓存:一旦高速缓存中的一个字被修改,则在主存中要立即修改 2. 写回高速缓存: 并不是立即写回,而是当被修改...

我类个擦
2014/10/02
0
0
微服务实战(二):落地微服务架构到直销系统(构建消息总线框架接口)

从上一篇文章大家可以看出,实现一个自己的消息总线框架是非常重要的内容,消息总线可以将界限上下文之间进行解耦,也可以为大并发访问提供必要的支持。 消息总线的作用: 1.界限上下文解耦:...

曹剑
2018/07/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Mysql(Mariadb)数据库主从复制

Mysql主从复制的实现原理图大致如下: MySQL之间数据复制的基础是以二进制日志文件(binary log file)来实现的,一台MySQL数据库一旦启用二进制日志后,其作为master,它数据库中所有操作都...

linux-tao
47分钟前
2
0
Mysql(Mariadb)数据库主从复制

Mysql主从复制的实现原理图大致如下: MySQL之间数据复制的基础是以二进制日志文件(binary log file)来实现的,一台MySQL数据库一旦启用二进制日志后,其作为master,它数据库中所有操作都...

Linux就该这么学
今天
2
0
Mysql(Mariadb)数据库主从复制

Mysql主从复制的实现原理图大致如下: MySQL之间数据复制的基础是以二进制日志文件(binary log file)来实现的,一台MySQL数据库一旦启用二进制日志后,其作为master,它数据库中所有操作都...

xiangyunyan
今天
2
0
Android 自定义Path贝塞尔曲线View实践——旋转的花朵

一、关于贝塞尔曲线 在工业设计方面贝塞尔曲线有很多用途,同样,在Android中,贝塞尔曲线结合Path类可以实现更复杂的图形,这里我们给一个案例,来实现一种旋转的花朵。对于贝赛尔曲线的理解...

IamOkay
今天
3
0
7、redis主从复制和sentinel配置高可用

一:redis主从配置 1、环境准备 master : 192.168.50.10 6179 slave1: 192.168.50.10 6279 slave2: 192.168.50.10 6379 2、redis.conf配置文件配置 master port 6179......

刘付kin
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部