围观:基于事件机制的内部解耦之心路历程

每篇文章都有属于它自己的故事,没有故事的文章是没有灵魂的文章。而我就是这个灵魂摆渡人。

主人公张某某,这边不方便透露姓名,就叫小张吧。小张在一家小型的互联网创业团队中就职。

职位是Java后端开发,所以整体和业务代码打交道在所难免。

之前有个搜索相关的需求,而且数量量也算比较大,就采用了ElasticSearch来做搜索。第一版由于时间比较赶,做的比较粗糙。越到后面发现代码越难写下去了,主要是在更新索引数据的场景没处理好,才有了今天的故事。

基础入门 Spring Event

Spring的事件就是观察者设计模式,一个任务结束后需要通知任务进行下一步的操作,就可以使用事件来驱动。

在Spring中使用事件机制的步骤如下:

自定义事件对象,继承 ApplicationEvent

自定义事件监听器,实现 ApplicationListener 或者通过 @EventListener 注解到方法上实现监听

自定义发布者,通过 applicationContext.publishEvent()发布事件

Spring Event在很多开源框架中都有使用案例,比如Spring Cloud中的Eureka里面就有使用

event包

围观:基于事件机制的内部解耦之心路历程

定义Event

围观:基于事件机制的内部解耦之心路历程

发布Event

围观:基于事件机制的内部解耦之心路历程

Guava EventBus

EventBus是Guava的事件处理机制,在使用层面和Spring Event差不多。这里不做过多讲解,今天主要讲Spring Event。

业务背景

所有的数据都会有一个定时任务去同步数据到ElasticSearch中,业务中直接从ElasticSearch查询数据返回给调用方。

之所以把所有数据都存入ElasticSearch是为了方便,如果只存储搜索的字段,那么搜索出来后就还需要去数据库查询其他信息进行组装。

就是由于所有数据都会存储ElasticSearch中,所以当这些数据发生变更的时候我们需要去刷新ElasticSearch中的数据,这个就是我们今天文章的核心背景。

假设我们ElasticSearch中的数据是文章信息,也就是我们经常看的技术文章,这个文章中存储了访问量,点赞量,评论量等信息。

当这些动作发生的时候,都需要去更新ElasticSearch的数据才行,我们默认的操作都是更新数据库中的数据,ElasticSearch是由定时任务去同步的,同步会有周期,做不到毫秒别更新。

实现方案-倔强青铜

倔强青铜就是在每个会涉及到数据变更的地方,去手动调用代码进行数据的刷新操作,弊端在于每个地方都要去调用,这还是简单的场景,有复杂的业务场景,一个业务操作可能会涉及到很多数据的刷新,也就是需要调用很多次,模拟代码如下:

// 浏览 public void visit() { articleIndexService.reIndex(articleId); XXXIndexService.reIndex(articleId); ........ } // 评论 public void comment() { articleIndexService.reIndex(articleId); } 实现方案-秩序白银

倔强青铜的弊端在于不解耦,而且是同步调用,如果在事务中会加长事务的时间。所以我们需要一个异步的方案来执行重建索引的逻辑。

经过大家激烈的讨论,而项目也是以Spring Boot为主,所以选择了Spring Event来作为异步方案。

定义一个重建文章索引的Event,代码如下:

public class ArticleReIndexEvent extends ApplicationEvent { private String id; public ArticleReIndexEvent(Object source, String id) { super(source); this.id = id; } public String getId() { return id; } }

然后写一个EventListener来监听事件,进行业务逻辑处理,代码如下:

@Component public class MyEventListener { @EventListener public void onEvent(ArticleReIndexEvent event) { System.out.println(event.getId()); } }

使用的地方只需要发布一个Event就可以,这个动作默认是同步的,如果我们想让这个操作不会阻塞,变成异步只需要在@EventListener上面再增加一个@Async注解。

// 浏览 public void visit() { applicationContext.publishEvent(new ArticleReIndexEvent(this, articleId)); applicationContext.publishEvent(new XXXReIndexEvent(this, articleId)); } // 评论 public void comment() { applicationContext.publishEvent(new ArticleReIndexEvent(this, articleId)); } 实现方案-荣耀黄金

秩序白银的方案在代码层面确实解耦了,但是使用者发布事件需要关注的点太多了,也就是我改了某个表的数据,我得知道有哪些索引会用到这张表的数据,我得把这些相关的事件都发送出去,这样数据才会异步进行刷新。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjfpj.html