[关闭]
@Rays 2018-07-06T17:11:12.000000Z 字数 1828 阅读 1282

使用Kafka Streams构建事件溯源系统的经验分享

架构&设计


摘要: 近期在乌克兰基辅举行的JEEConf大会上,Amitay Horwitz介绍了他的团队是如何实现一个事件溯源的发票系统、系统两年半生产环境运行期间所遇到的挑战,以及团队是如何使用Kafka Streams实现新的设计。

作者: Jan Stenberg

正文:

近期在乌克兰基辅举行的JEEConf大会上,Amitay Horwitz介绍了他的团队是如何实现一个事件溯源的发票系统、系统两年半生产环境运行期间所遇到的挑战,以及团队是如何使用Kafka Streams实现新的设计。

Horwitz是Wix的一位软件工程师,他与团队一起在2015年着手实现一种新的发票服务,帮助客户在线管理发票并接收付款。在设计新服务时,他们设想能创建一种小规模的简单软件库,具有非侵入式的、能维护数据的完整性、易于添加客户视图等能力。为实现上述目标,团队决定使用事件溯源架构实现服务。

尽管团队努力实现一种简单的设计,但最终软件库还是变得相当庞大。团队在此过程中也碰上了问题,由于读写最终一致性的问题,客户时常无法看到新建立的发票。虽然创建发票的请求更新写模型加入了新发票信息,但此后的请求是从尚未更新的写模型中读取的,因此并未包括新发票信息。

其中最主要的问题在于如何重构视图。在添加新事件处理器时,需确保对传递而来数据的处理要先于对新事件的处理,并在没有事件进入的情况下触发重构。该机制的实现已被证实要比团队先前的设想更为复杂,尤其对于团队所面对的分布式环境,其中的事件来自于各个服务器。这些问题促使Horwitz考虑寻求采用另一种能保持事件溯源优点的替代架构。

在Horwitz看来,Kafka是一种有副本的、容错的、分布式的只添加日志,常用于“发布者-订阅者”模式,或是作为队列使用,他指出Kafka还可以实现更多的功能。Kafka的基本结构称为主题(Topic),它是一种分区的逻辑队列。发布者根据消息中的键值将消息推送到各个分区,进而消费者可以消费这些消息。事件溯源系统具有两个重要关键特性,分别是使用单一分区维护消息的顺序,以及消息可在被消费后得到存储。

Kafka Streams为Kafka添加了流处理能力。它提供了两种抽象:

在使用Kafka的发票系统新设计中,团队实现了一个快照状态存储,用于保存每个聚合的当前状态。当从命令流中接收到一个命令后,命令处理器从状态存储中读取相应聚合的当前状态。进而处理器可以决定命令状态是成功还是失败,并通过结果流返回结果。如果命令处理成功,那么系统将创建事件,推送到事件存储并读取新事件的数据流,然后更新状态存储中的聚合状态为新状态。Horwitz指出,可以使用非常精确和声明式方式编写命令处理器逻辑。在他给出的例子中,仅使用了60行的Scala代码。

Kafka是新架构的核心,其中微服务与Kafka通信,而且微服务间也是通过Kafka通信的。系统还可推送信息到Kafka,或是在创建分析报告实例时从Kafka获取信息。Horwitz总结了新设计的多个优点:

InfoQ的一次采访中,Horwitz提及尽管他们已在生产中大量地使用了Kafka,但是新设计依然处于评估阶段。他指出,有人认为Kafka并不适用于CQRS和事件溯源系统,但是他认为可在明确权衡考虑的情况下充分使用Kafka。如果用户希望能保存具有客户各种属性的页面浏览事件,那么就可以轻易地根据这些信息创建聚合。Horwitz认为这符合事件溯源的形式,Kafka非常适用于此。

如果以聚合标识作为分区键值,那么同一聚合的所有命令最终将位于同一命令主题分区中,并将使用单线程按序处理。这种方式确保了在如果没有处理生成所有下游(downstream)事件的前一个命令,当前命令不会得到处理。Horwitz指出,该方式建立了强一致性保证。

查看英文原文: Experiences from Building an Event-Sourced System with Kafka Streams

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注