事件驱动是一种灵活的系统设计方法,在事件驱动的系统中,当数据发生变化时系统会产生、发布一个对应的事件,其它对这个事件感兴趣的部分会接收到通知,并进行相应的处理。事件驱动设计最大的好处在我看来有两点:一是它为系统提供了很好的扩展能力,比如我们可以对某类事件增加一个订阅者来对系统进行扩展,最主要的是我们并不需要修改任何已有的代码,它完全符合开闭原则;二是它实现了模块间的低偶合,系统间各个部分不是强依赖关系,而是通过事件把整个系统串联起来。
当然,任何事务都有两面性,事件驱动也有其不好的方面。首先,实现一套这样的系统复杂度就很高,对开发人员的要求也很高;再次,对系统的整体把控会很困难,想象一下面对几百个类别的事件,并且没有一个统一的地方可以让我们看到整个业务处理流程,会是什么心情?所以当我们决定采用事件驱动实现系统中,一定要维护好相关的文档,并保持它们的有效性。
我们再来看看事件驱动架构的一些其它的优点:
-
更好的响应性
事件驱动中,事件的响应是异步处理的,所以它具有更好的响应性。
-
更好的容错性
业务主流程在发布事件之后便结束了,扩展流程的延后处理可以异步不断的失败重试,直到成功为止,系统整体容错性更强。
设计篇
首先,我们需要定义什么是事件?从业务角度看,事件包括以下属性:
属性 | 字段 | 类型 | 说明 |
标识 | ID | string | 系统内部每个事件都需要一个唯一的标识。 |
类型 | eventType | string | 数据发生变化产生事件,不同类型的数据变化产生不同类型的事件。比如会员下单、会员注册、用户修改手机号等等。 |
时间 | eventTime | datetime | 即数据发生变化的时间。 |
上下文 | context | string | 事件发生时的上下文信息。比如会员修改手机号事件,需要原号码和新号码,会员 ID 等信息。 |
接下来,我们看看如何设计一套基于事件驱动的系统,你知道设计模式中的观察者模式吗?
观察者模式 :定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式天生就是事件驱动的一个实现,但是直接使用它有很多的弊端。首先,它是基于主题的,有多少类事件就需要多少个主题类,这可能会导致类爆炸;其次,观察者模式是同步实现的,这样我们可能会牺牲掉响应性和容错性等优势。
所以我们需要对观察者模式稍作改进,我们分别从事件发发布和消费两个方面来分析。
事件的发布
本文的标题是《基于 Kafka 实现事件驱动架构》,很明显,我们使用 kafka 作为消息中间件来传递事件消息。所以,像修改会员手机号码的代码可能实现如下:
@Transactional(readOnly =false, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) @Override publicvoidchangePhoneNumber(String newNumber){ userDao.updatePhone(this.getUserId(), newNumber);// 本地数据库修改 // 发布 用户手机号码变更 事件 Event event =newEvent(...);// 创建一个事件对象,表示用户修改手机号码 ProducerRecord record =newProduce