设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123425 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

57 | 观察者模式(下):如何实现一个异步非阻塞的EventBus框架?

进程间
进程内
异步非阻塞
同步阻塞
UserController的重构建议
EventBus的作用
框架的作用
AsyncEventBus类
EventBus类
ObserverRegistry类
ObserverAction类
Subscribe注解
ObserverRegistry类
ObserverAction类
Subscribe注解
AsyncEventBus类
EventBus类
实现示例
功能
Google Guava EventBus
问题与改进
第二种实现方式
第一种实现方式
实现方式
应用场景
原理
课堂讨论
重点回顾
手把手实现一个EventBus框架
EventBus框架
EventBus框架功能需求介绍
异步非阻塞观察者模式的简易实现
观察者模式
如何实现一个异步非阻塞的EventBus框架

该思维导图由 AI 生成,仅供参考

上一节课中,我们学习了观察者模式的原理、实现、应用场景,重点介绍了不同应用场景下,几种不同的实现方式,包括:同步阻塞、异步非阻塞、进程内、进程间的实现方式。
同步阻塞是最经典的实现方式,主要是为了代码解耦;异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率;进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。
今天,我们聚焦于异步非阻塞的观察者模式,带你实现一个类似 Google Guava EventBus 的通用框架。等你学完本节课之后,你会发现,实现一个框架也并非一件难事。
话不多说,让我们正式开始今天的学习吧!

异步非阻塞观察者模式的简易实现

上一节课中,我们讲到,对于异步非阻塞观察者模式,如果只是实现一个简易版本,不考虑任何通用性、复用性,实际上是非常容易的。
我们有两种实现方式。其中一种是:在每个 handleRegSuccess() 函数中创建一个新的线程执行代码逻辑;另一种是:在 UserController 的 register() 函数中使用线程池来执行每个观察者的 handleRegSuccess() 函数。两种实现方式的具体代码如下所示:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文详细介绍了如何实现一个异步非阻塞的EventBus框架,通过讲解异步非阻塞观察者模式的实现方式和Google Guava EventBus的功能需求,展示了如何利用EventBus框架实现观察者模式。文章通过实例和代码演示,清晰地阐述了EventBus框架的功能和使用方法。重点介绍了EventBus、AsyncEventBus、Subscribe、ObserverAction和ObserverRegistry等几个主要类和函数的实现原理和作用。通过不到200行代码的实现,展示了如何实现一个凑活能用的EventBus,从功能上与Google Guava EventBus几乎一样。总结指出,框架的作用在于隐藏实现细节、降低开发难度、做到代码复用、解耦业务与非业务代码,让程序员聚焦业务开发。同时,鼓励读者在业务开发中善于抽象非业务的、可复用的功能,并积极地把它们实现成通用的框架。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(80)

  • 最新
  • 精选
  • Lambor
    使用异步非阻塞观察者模式,事务怎么控制呢?毕竟最后都是扔到线程池里执行。

    作者回复: 很难保证 需要更复杂的措施

    2020-04-27
  • 蚂蚁内推+v
    老师,例子跑不通,应该把long 类型参数改为封装类型Long ,不然找不到对应的订阅者

    作者回复: 多谢指出,我改下!

    2020-04-15
  • 小文同学
    Guava EventBus 对我来说简直是一份大礼。里面解耦功能使本来的旧项目又不可维护逐渐转化为可维护。 EventBus作为一个总线,还考虑了递归传送事件的问题,可以选择广度优先传播和深度优先传播,遇到事件死循环的时候还会报错。Guava的项目对这个模块的封装非常值得我们去阅读,复杂的都在里头,外面极为易用,我拷贝了一份EventBus的代码进行修改以适配自己的项目,发觉里面的构造都极为精密巧妙,像一个机械钟表一样,自己都下不了手,觉得不小心就是弄坏了。 跟随真正优秀的工程师,并阅读其写出来的代码让人受益匪浅。
    2020-03-13
    1
    216
  • 下雨天
    课后题: 代理模式,使用一个代理类专门来处理EventBus相关逻辑。作用: 1.将业务与非业务逻辑分离 2.后续替换EventBus实现方式直接改写代理类,满足拓展需求
    2020-03-13
    4
    83
  • zhengyu.nie
    一开始在携程工作的时候因为早期Spring Event驱动强制要求事件继承抽象事件,而转到Guava EventBus,在Event实体上更加灵活。后面来阿里后发现一些项目里,Spring新版本也可以支持非继承的事件类型了,也有很多MetaQ消息直接分发到内存Event的写法。 关于EventBus源码也看了几遍了,总体来说提供了几种dispatcher,有广度和深度优先原则,像PerThread中两层while也有对嵌套事件的处理,像Google工程师致敬。 EventBus现在来对我个人说主要有以下几点可能存在的问题: 1.在比较高需求的场景上,Event持久化机制也是需要的,不管是为了高可用(内存队列宕机就丢),做成最终一致性软事务,或者是CQRS中事件溯源等需求。 2.现在的异步处理,是直接丢在同一个线程池处理,那么存在忙死的event导致event饿死的情况,所以这一块会有很大局限性,对比akka之类的话。 3.现在的Event在没打的@AllowConcurrentEvents时候,也就是需要线程安全的时候,是invoke method过程是加了synchronized关键字控制的,那么最好方法粒度不要太大,性能上考虑的话。 其实现在也蛮纠结的,到底用EventBus还是Spring Event,按道理讲,现在项目基本都是SpringBoot体系,那么其实Spring事件隔绝依赖更多,也更容易和Spring Async等集成,所以我现在基本是用Spring事件驱动替代EventBus。
    2020-04-14
    2
    69
  • 鱼Shiyao
    把老师的EventBus的代码实现了一下,发现有两个地方有问题。 1: XMsg xMsg = new XMsg(); YMsg yMsg = new YMsg(); 如果XMsg是YMsg的父类的话,应该是 post(xMsg); => AObserver接收到消息 post(yMsg); => AObserver,BObserver接收到消息 2. 和刚才的问题一样,对应着ObserverRegistry的代码。 在getMatchedObserverAction函数中 if (postedEventType.isAssignableFrom(eventType)) 应该改成 if (eventType.isAssignableFrom(postedEventType))
    2020-03-18
    9
    47
  • 辣么大
    重构使用代理模式,将非业务代码放到代理类中。 另外试了争哥讲的EventBut类,在定义观察者的入参要修改成*Long*类型,如果使用long,这个方法是无法注册的,代码执行收不到通知。应该是ObserverRegistry类需要完善一下。 @Subscribe public void handleRegSuccess(Long userId) { System.out.println("handleRegSuccess..."); promotionService.issueNewUserExperienceCash(userId); } 代码见:https://github.com/gdhucoder/Algorithms4/tree/master/designpattern/u57
    2020-03-13
    4
    12
  • 小晏子
    我的想法比较直接,将UserController中的业务代码提出来放在接口的实现类中,这个UserController可以改名为EventController,然后这个接口实现类注入到这个EventController中,这样业务逻辑和控制逻辑就分离了,示例如下: interface iController { object register() } public class UserControllerService implement iController { private string telphone; private string password; public Long register() { long userId = userService.register(telephone, password); return userId; } } public class EventController { private iController iService; private EventBus eventBus; private static final int DEFAULT_EVENTBUS_THREAD_POOL_SIZE = 20; public EventController() { eventBus = new AsyncEventBus(Executors.newFixedThreadPool(DEFAULT_EVENTBUS_THREAD_POOL_SIZE)); // 异步非阻塞模式 } public void setRegObservers(List<Object> observers) { for (Object observer : observers) { eventBus.register(observer); } } public void SendMessage() { object msg = iService.register() eventBus.post(msg) } }
    2020-03-13
    1
    9
  • blacknhole
    提个问题: 文中“所谓可匹配指的是,能接收的消息类型是发送消息(post 函数定义中的 event)类型的子类”这话似乎有问题,应该是父类吧?
    2020-03-15
    2
    7
  • Heaven
    对于这个问题,在UserCntroller中,我们应该只保留post函数() 发送的相关逻辑,而将注册Observer,初始化EventBus相关逻辑剔除,如果非要使用EventBus来实现的话,我们需要有人帮我们去进行注册和初始化,这时候就可以立马想到之前讲的工厂模式的DI框架,我们可以让所有观察者都被DI框架所管理,并且对EventBus创建一个装饰器类,在这个装饰器类中,由开发者选择注入线程池实现异步发送还是直接使用同步发送的,并且在init函数中 从DI框架管理的对象池中拿出所有标有@Subscribe注解的类,保存到ObserverRegistry中,对于所有需要使用EventBus的类,注入这个装饰器类即可,设计的好,甚至可以做到其他依赖代码都不用改一点
    2020-03-13
    1
    7
收起评论
显示
设置
留言
80
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部