57 | 观察者模式(下):如何实现一个异步非阻塞的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
《设计模式之美》,新⼈⾸单¥98
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(80)
- 最新
- 精选
- Lambor使用异步非阻塞观察者模式,事务怎么控制呢?毕竟最后都是扔到线程池里执行。
作者回复: 很难保证 需要更复杂的措施
2020-04-27 - 蚂蚁内推+v老师,例子跑不通,应该把long 类型参数改为封装类型Long ,不然找不到对应的订阅者
作者回复: 多谢指出,我改下!
2020-04-15 - 小文同学Guava EventBus 对我来说简直是一份大礼。里面解耦功能使本来的旧项目又不可维护逐渐转化为可维护。 EventBus作为一个总线,还考虑了递归传送事件的问题,可以选择广度优先传播和深度优先传播,遇到事件死循环的时候还会报错。Guava的项目对这个模块的封装非常值得我们去阅读,复杂的都在里头,外面极为易用,我拷贝了一份EventBus的代码进行修改以适配自己的项目,发觉里面的构造都极为精密巧妙,像一个机械钟表一样,自己都下不了手,觉得不小心就是弄坏了。 跟随真正优秀的工程师,并阅读其写出来的代码让人受益匪浅。2020-03-131216
- 下雨天课后题: 代理模式,使用一个代理类专门来处理EventBus相关逻辑。作用: 1.将业务与非业务逻辑分离 2.后续替换EventBus实现方式直接改写代理类,满足拓展需求2020-03-13483
- 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-14269
- 鱼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-18947
- 辣么大重构使用代理模式,将非业务代码放到代理类中。 另外试了争哥讲的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/u572020-03-13412
- 小晏子我的想法比较直接,将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-1319
- blacknhole提个问题: 文中“所谓可匹配指的是,能接收的消息类型是发送消息(post 函数定义中的 event)类型的子类”这话似乎有问题,应该是父类吧?2020-03-1527
- Heaven对于这个问题,在UserCntroller中,我们应该只保留post函数() 发送的相关逻辑,而将注册Observer,初始化EventBus相关逻辑剔除,如果非要使用EventBus来实现的话,我们需要有人帮我们去进行注册和初始化,这时候就可以立马想到之前讲的工厂模式的DI框架,我们可以让所有观察者都被DI框架所管理,并且对EventBus创建一个装饰器类,在这个装饰器类中,由开发者选择注入线程池实现异步发送还是直接使用同步发送的,并且在init函数中 从DI框架管理的对象池中拿出所有标有@Subscribe注解的类,保存到ObserverRegistry中,对于所有需要使用EventBus的类,注入这个装饰器类即可,设计的好,甚至可以做到其他依赖代码都不用改一点2020-03-1317
收起评论