不一样的事件总线 - NextEvents

原创
2016/04/11 10:46
阅读数 273

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) 2749 47ms 727ms 2000
SharedThreads(1ms Payload) 3012 8ms 663ms 2000
CallerThread(1ms Payload) 739 2705ms 2705ms 2000
GuavaEvents(1ms Payload) 717 2785ms 2785ms 2000
MultiThreads(Nop Payload) 1947582 1026ms 1026ms 2000000
SharedThreads(Nop Payload) 891257 2243ms 2244ms 2000000
CallerThread(Nop Payload) 3933523 508ms 508ms 2000000
GuavaEvents(Nop Payload) 2083789 959ms 959ms 2000000

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) 3242 22ms 616ms 2000
SharedThreads(1ms Payload) 3351 3ms 596ms 2000
CallerThread(1ms Payload) 914 2187ms 2187ms 2000
GuavaEvents(1ms Payload) 890 2245ms 2245ms 2000
MultiThreads(Nop Payload) 1078525 1846ms 1854ms 2000000
SharedThreads(Nop Payload) 1373540 1427ms 1456ms 2000000
CallerThread(Nop Payload) 3834583 521ms 521ms 2000000
GuavaEvents(Nop Payload) 1578518 1266ms 1267ms 2000000

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) 3447 10ms 580ms 2000
SharedThreads(1ms Payload) 3376 1ms 592ms 2000
CallerThread(1ms Payload) 902 2215ms 2215ms 2000
GuavaEvents(1ms Payload) 884 2260ms 2260ms 2000
MultiThreads(Nop Payload) 1935508 1033ms 1033ms 2000000
SharedThreads(Nop Payload) 2261975 882ms 884ms 2000000
CallerThread(Nop Payload) 4602303 434ms 434ms 2000000
GuavaEvents(Nop Payload) 3980131 502ms 502ms 2000000
展开阅读全文
打赏
1
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
1
分享
返回顶部
顶部