项目中应用eventbus解决的问题
在项目开发过程中,往往有些功能表面看起来简单,但实际开发的结果非常复杂,仔细分析下原因发现很多都是因为附加了许多的额外功能。
文章图片
真的简单吗?
比如我们对一个电商平台的商品数据做修改的功能来讲,其实非常简单,无非就是运营人员在管理平台中对商品进行修改数据,然后点击提交,核心功能的确很简单,但可能有人会要求对商品的修改都需要增加操作日志,还有人提出需要在商品数据修改后自动去更新检索系统中的数据,有人提要在商品数据修改后需要经过审核人的审核才能生效,还有人提需要给运营人员发邮件通知等等,如此一来这个商品修改的功能就不是那么简单了,它除了完成自己的使命,还需要去调用其它的服务来完成,活动类似如下:
文章图片
横向的主流程,上下四个小方框是附加功能,复杂的原因包含如下两点:
- 附加功能导致功能开点变多,工作量加大
- 附加功能导致程序逻辑复杂,在程序中需要去访问其它的服务,还需要考虑数据完整性,性能,服务依赖等各种问题。
@Autowired private SearchService searchService; @Autowired private MailService mailService; @Autowired private ProductLogService productLogService; //如下下保存商品的代码片段 itemDao.save(product); searchService.update(product.getId()); mailService.post(product.getId()); logService.log(product.getId());
问题如下:
- 对无业务直接关联的服务强耦合
- 可能存在性能问题,比如你需要关心邮件发送是否会影响主流程
- 代码可读性变差,过多的逻辑容易导致分不清主体核心功能
注:guava的eventbus是个进程内级别的,无法跨进程,后面我抽时间再整理下基于消息队列的分布式事件总结。
文章图片
【项目中应用eventbus解决的问题】
这里我并不介绍如何使用EventBus,而是重点来说明我们项目中对它的应用,哪些是做的不好的地方。
第一:要有观察者,这里我们根据不同的业务封装不同的观察者,下面是更新检索系统数据的类
@Service public class SearchEventListener { @Autowired() ProductUpdateMgr productSearchUpdateMgr; private final static Logger logger = LoggerFactory.getLogger(SearchEventListener.class); @Subscribe public void listen(String itemLegacyId) { try { productSearchUpdateMgr.markProductDirty(itemLegacyId); } catch(Exception ex) { logger.error("更新检索异常:" + ex.getMessage() + ex.getStackTrace()); } } }
第二:需要有将观察者注册到eventbus中去,我们专门写了一个类来做这件事情,完成两件事情:
- 注册观察者到eventbus中
- 进一步包装post方法以便调用者以服务形式调用,让productService依赖eventbus而不是依赖实际的检索服务,邮件服务等
@Service public class EventListenerManager { EventBus mmsProductEventBus; EventBus mmsItemEventBus; EventBus mmsSearchEventBus; EventBus mmsRebuildAllSearchEventBus; EventBus mmsCommonLogEventBus; @Autowired ItemEventListener itemListener; @Autowired ProductEventListener productListener; @Autowired SearchEventListener searchListener; @Autowired RebuildAllSearchEventListener rebuildAllSearchListener; @Autowired MmsCommonLogEventListener commonLogListener; @PostConstruct private void init() { mmsProductEventBus = new EventBus(); mmsItemEventBus = new EventBus(); mmsSearchEventBus = new EventBus(); mmsRebuildAllSearchEventBus=new EventBus(); mmsCommonLogEventBus=new EventBus(); mmsItemEventBus.register(itemListener); mmsProductEventBus.register(productListener); mmsSearchEventBus.register(searchListener); mmsRebuildAllSearchEventBus.register(rebuildAllSearchListener); mmsCommonLogEventBus.register(commonLogListener); }public void notifyItemLog(Long itemId) { mmsItemEventBus.post(itemId); }public void notifyProductLog(Long productId) { mmsProductEventBus.post(productId); }public void notifyUpdateSearch(String itemLegacyId) { mmsSearchEventBus.post(itemLegacyId); } public void notifyRebuildAllSearch() { mmsRebuildAllSearchEventBus.post(""); } public void notifyAddCommonLog(MmsCommonLogModel log) { mmsCommonLogEventBus.post(log); } }
文章图片
上面的实现以及我们在刚学习使用guava eventbugs时遇到哪些问题呢?
问题一:为什么上面的代码有这么多的eventbus而不是一个呢?注意下enventbus的post方法,我们再看下它的源码:它是根据参数的类型来找观察者注册的方法的,而我们写的观察者类中的方法中的参数都是一些primitive类型的,总共有10个左右方法,要想根据参数类型来正确的在一个eventbus中识别调用哪个方法,是比较困难的。
public void post(Object event) { Set> dispatchTypes = flattenHierarchy(event.getClass()); boolean dispatched = false; for (Class> eventType : dispatchTypes) { subscribersByTypeLock.readLock().lock(); try { Set wrappers = subscribersByType.get(eventType); if (!wrappers.isEmpty()) { dispatched = true; for (EventSubscriber wrapper : wrappers) { enqueueEvent(event, wrapper); } } } finally { subscribersByTypeLock.readLock().unlock(); } }if (!dispatched && !(event instanceof DeadEvent)) { post(new DeadEvent(this, event)); }dispatchQueuedEvents(); }
如何解决?可以针对每个方法的参数封装一个类,比如更新检索的方法参数叫SearchChangeEvent,发送审核邮件的参数叫ApprovalChangeEvent等等,这样我们就可以将所有的观察者注册到一个eventbus中,调用post方法时就不会出现问题了,最后简化后的结果如下:
@Autowired EventListenerManager eventManager; //如下下保存商品的代码片段 itemDao.save(product); eventManager.post(new SearchChangeEvent(product.getId)); eventManager.post(new MailChangeEvent(product.getId)); eventManager.post(new LogChangeEvent(product.getId));
问题二:性能问题,之前有提到过附加的功能会导致原本单纯的事物不单纯,比如调用某些服务时可能影响整体性能,当时我们想当然的认为使用了enventbus本身就是异步的,其实如果用eventbus它是同步的,要想使用异步需要使用这个类来完成AsyncEventBus。
问题三:强耦合问题,由于有了EventListenerManager,我们在具体业务中就不需要依赖不直接相关的服务了,只需要依赖EventListenerManager这个看起来与任务业务都无关的管理类就可以了。
文章图片
通过实际项目中对eventbus的应用来分析它能解决的问题以及当初应用有待提高的地方。很显示eventbus应用得当可以简化程序复杂性,提高代码可读性,降低开发维护成本。
转载于:https://www.cnblogs.com/ASPNET2008/p/5169690.html
推荐阅读
- Docker应用:容器间通信与Mariadb数据库主从复制
- JS中的各种宽高度定义及其应用
- 赢在人生六项精进二阶Day3复盘
- 六项精进20180530
- 2020-12(完成事项)
- 17|17 关山松 第二课作业#公众号项目# D20
- RxJava|RxJava 在Android项目中的使用(一)
- java之static、static|java之static、static final、final的区别与应用
- Android7.0|Android7.0 第三方应用无法访问私有库
- GIS跨界融合赋能多领域技术升级,江淮大地新应用成果喜人