在项目开发过程当中,每每有些功能表面看起来简单,但实际开发的结果很是复杂,仔细分析下缘由发现不少都是由于附加了许多的额外功能。html
真的简单吗?设计模式
好比咱们对一个电商平台的商品数据作修改的功能来说,其实很是简单,无非就是运营人员在管理平台中对商品进行修改数据,而后点击提交,核心功能的确很简单,但可能有人会要求对商品的修改都须要增长操做日志,还有人提出须要在商品数据修改后自动去更新检索系统中的数据,有人提要在商品数据修改后须要通过审核人的审核才能生效,还有人提须要给运营人员发邮件通知等等,如此一来这个商品修改的功能就不是那么简单了,它除了完成本身的使命,还须要去调用其它的服务来完成,活动相似以下:app
横向的主流程,上下四个小方框是附加功能,复杂的缘由包含以下两点:异步
刚开始咱们在项目中开发时,使用的就是最简单的强耦合去直接调用服务,好比在调用完数据保存的方法后,去调用logService的log方法记录日志,调用esService的方法去更新检索系统,调用mailService的方法去发邮件,这样会致使咱们的productService强耦合这些与商品保存逻辑没有直接关联的服务,这样看起来商品保存的功能变得不那么单纯,也就是咱们文前提到的复杂了,会出现相似代码:分布式
@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());
问题以下:post
若是解决呢?有一个设计模式能够解决,那就是观察者模式,以前学习.net时专门写过一篇(老生常谈:观察者模式),里面提到有传统的实现方式以及事件机制,事件机制的实现比较简单一些,这里咱们在解决这个问题时引用了guava组件中提供的eventbus,它与以前那篇观察者模式的实现很类似,看下面这张图,功能之间没有错综复杂的依赖。性能
注:guava的eventbus是个进程内级别的,没法跨进程,后面我抽时间再整理下基于消息队列的分布式事件总结。学习
这里我并不介绍如何使用EventBus,而是重点来讲明咱们项目中对它的应用,哪些是作的很差的地方。
第一:要有观察者,这里咱们根据不一样的业务封装不一样的观察者,下面是更新检索系统数据的类ui
@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中去,咱们专门写了一个类来作这件事情,完成两件事情:this
@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<Class<?>> dispatchTypes = flattenHierarchy(event.getClass()); boolean dispatched = false; for (Class<?> eventType : dispatchTypes) { subscribersByTypeLock.readLock().lock(); try { Set<EventSubscriber> 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应用得当能够简化程序复杂性,提升代码可读性,下降开发维护成本。