5.2 事件数据持久化
意义
- 系统之间数据对账
- 实现发布方和订阅方事件数据的审计
- 当遇到MQ、订阅方系统宕机或网络中断,在问题解决后仍可继续后续业务流转,保证数据一致性。
毕竟虽然MQ都有持久化功能,但中间过程或在订阅到数据后,在处理之前出问题,需要进行数据对账,这样就没法找到发布时和处理后的数据版本。关键的业务数据推荐还是落库。
实现方案
- 持久化到本地业务DB的事件表,利用本地事务保证业务和事件数据的一致性
- 持久化到共享的事件DB。业务、事件DB不在同一DB,它们的数据持久化操作会跨DB,因此需分布式事务保证业务和事件数据强一致性,对系统性能有影响
5.3 事件总线(EventBus)
意义
实现同一微服务内的聚合之间的领域事件,提供事件分发和接收等服务。
是进程内模型,会在微服务内聚合之间遍历订阅者列表,采取同步或异步传递数据。
因为在微服务内部在同一个进程,事件总线相对好配置,它可以配置为异步的也可以配置为同步的。如果是同步就不需要落库。推荐少用微服务内聚合之间的领域事件,它会增加开发复杂度。
而微服务之间的事件,在事件数据落库后,通过应用服务直接发布到MQ。
事件分发流程
- 若是微服务内的订阅者(其它聚合),则直接分发到指定订阅者
- 微服务外的订阅者,将事件数据保存到事件库(表)并异步发送到MQ
- 同时存在微服务内和外订阅者,则先分发到内部订阅者,将事件消息保存到事件库(表),再异步发送到MQ
5.4 MQ
跨微服务的领域事件大多会用到MQ,实现跨微服务的事件发布和订阅。
虽然MQ自身有持久化功能,但中间过程或在订阅到数据后,在处理之前出问题,需要进行数据对账,这样就没法找到发布时和处理后的数据版本。关键的业务数据推荐还是落库。
5.5 接收&&处理
微服务订阅方在应用层采用监听机制,接收MQ中的事件数据,完成事件数据的持久化后,就可以开始进一步的业务处理。领域事件处理可在领域服务中实现。
- 事件是否被消费成功(消费端成功拿到消息或消费端业务处理成功),如何通知消息生产端?
因为事件发布方有事件实体的原始的持久化数据,事件订阅方也有自己接收的持久化数据。一般可以通过定期对账的方式检查数据的一致性。
在采取最终一致性的情况下,事件消费端如果出现错误,消费失败,但之前的业务都成功了,虽然记录了event dB,但后续如何处理,人工介入吗?如果人工介入再解决,前端用户会不会看到数据不一致,体验不好?
失败的情况应该比例是很少的。失败的信息可采用多次重试,如果这个还解决不了,只能将有问题的数据放到一个问题数据区,人工解决。当然要确保一个前提,要保证数据的时序性,不能覆盖已产生的数据。
一般发布方不会等待订阅方反馈结果。发布方有发布的事件表,订阅方有消费事件表,可采用对账方式发现问题数据。
管理
大型系统的领域事件有很多:
- 做好源端和目的端数据的对账处理,发现并识别处理过程中的异常数据
- 异步的方式一般都有源端和目的端定期对账的机制。比如采用类似财务冲正的方式。如果在发布和订阅之间事件表的数据发现异步数据有问题,需要回退,会有相应的代码进行数据处理,不过不同的场景,业务逻辑会不一样,处理的方式会不一样。有的甚至还需要转人工处理。
发现异常数据后,要有相应的处理机制
选择适合自己场景的技术,保证数据正确传输
6 总结
领域事件在设计时我们要重点关注领域事件,用领域事件来驱动业务的流转,尽量采用基于事件的最终一致,降低微服务之间直接访问的压力,实现微服务之间的解耦,维护领域模型的独立性和数据一致性。
领域事件驱动机制可实现一个发布方N个订阅方的模式,这在传统的直接服务调用设计中基本是不可能做到的。
领域事件 V.S CQRS
CQRS主要是想读写分离,将没有领域模型的查询功能,从命令中分离出来。领域事件主要目的还是为了微服务解耦,在连续的业务处理过程中,以异步化的方式完成下一步的业务处理,降低微服务之间的直连。
它们的共同点就是通过消息中间件实现从源端数据到目的端数据的交互和分离。
如果你就是不想用领域事件,聚合之间还可以通过应用层来协调和交互。应用服务是所有聚合之上的服务,负责服务的组合和编排,以及聚合之间的协调。
参考
《实现领域驱动设计》
《领域驱动设计》