为什么我们放弃使用Kafka Streams实现全部的事件溯源?-Mateusz


我们并不是说事件溯源总是一个糟糕的选择。这是一个真正强大的概念。但是,您应该警惕它可能给您的项目增加的复杂性。我们的看法是你不应该用它来解决所有类型的问题,而应该将它应用到选定的业务领域(它可能对所有类型的技术和技巧都是通用的)。
Kafka Streams 可能对我们来说更加陡峭的学习曲线,尽管我们仍然相信它可能是使用 Kafka 和 Java 进行数据流传输的绝佳解决方案。
 
产品
在不透露太多信息的情况下,我可以分享一下,我们正在谈论构成更大系统一部分的金融科技产品。其主要目的是跟踪用户的资金流向,处理银行转账和其他金融业务。
在改造之前,该软件使用事件溯源并使用 Kafka 作为真相的来源。我们大量使用 Kafka Streams 库来处理、聚合和存储数据。该堆栈基于:

  • Spring Boot
  • Kafka Streams, Spring Kafka,
  • Spring Web, Data, Security
  • PostgreSQL (just for query-only projections)
  • 模块化单体架构
  • AWS
  • Protobuf

但是我们现在从基于 Kafka Streams 的事件溯源切换到以数据库作为数据存储的事件驱动系统。

  • 以前,Kafka是我们的真理之源。所有代表系统中发生的变化的消息(事件)都被无限期地存储在Kafka中。
    现在,好的旧数据库存储了实体的当前状态,而Kafka只是帮助我们在变化发生后传播信息。我们对待Kafka更像一个消息中介,所以消息是短暂的。
  • 我们引入了同步的REST APIs,之前所有的东西都是通过Kafka进行异步的。
  • 到目前为止,我们不再使用Kafka Streams库,而是使用标准的消费者和生产者,这已经足够。
  • 我们从Protobuf切换到Avro
  • 这根本不是什么变化,但我们决定继续实施事件承载状态转移模式。
  • 对于一个单一的用例,我们将能够模拟事件溯源:我们将关键的消息存储在数据库中,我们可以将它们转储到Kafka上,以备我们需要重新播放所有的消息。

好处:

  • 新的期望是比最初假设的更快地实现更大的规模。这将意味着更少的时间来实际学习使用和操作事件源系统。
    因此,事件的数量最终会更大,这反过来会导致更长的重播时间来从事件中重建状态。这可以通过增加分区数量来解决,但是更改分区数量并不是那么容易。
  • 备份存储在数据库中的数据比备份 Kafka 消息和偏移量要容易得多。许多经过实战考验的解决方案都是现成的。
  • 在使用Kafka Streams时,我们在实现零停机时间方面遇到了一些麻烦(在新版本中可能有所改进)。REST+DB让它变得非常容易。
  • 事件源很困难(尤其是与Kafka流结合时)。很难很好地学习这个概念,当涉及到实施时,很难向其他人解释它。为了我们的新团队成员,也为了其他工程师,更简单的方法似乎更明智。
  • 我们了解到,对事件来源的系统进行推理比较困难。在一个潜在的危机中,可能需要更长的时间来发现发生了什么并恢复服务。

 
一致性
以前只在Kafka中存储所有的数据,我们能够使用Kafka Transactions来实现精确的一次性语义,从而实现一致性。
现在,当我们将变化的结果存储在数据库中,并需要将变化的事件发布到Kafka中时,我们无法在一个事务中完成。
我们正在考虑使用Outbox模式来规避它。
更多发件箱
  
审计跟踪
以前因为我们无限期地存储了所有的事件,所以我们有一个完整的历史,可以了解手头系统中发生的事情。因此,不需要审计跟踪表或其他审计日志--没有信息丢失。
现在,我们需要格外小心,不要失去对系统中发生的重要行动的追踪,因为有些行动可能只作为短暂的Kafka消息留下痕迹。
数据库只存储了系统的当前状态。
幸运的是,有行之有效的方法来解决这个问题。

新服务:历史数据
引入需要历史数据的新服务在以前是很简单的。他们可以直接从最早的偏移量中消费事件,然后重放历史。
有了新的事件驱动的解决方案,这种情况将更难处理。

尝试一种新的方法并发展你的技能总是很有趣的。但作为工程师,我们同时也需要务实,不断地重新评估我们的选择。
虽然围绕着在数据库中存储数据的比较传统的解决方案看起来比较枯燥,但从另一方面来说,它可以让我们把更多的时间花在业务功能上,而减少技术上的困难。