将Kafka用作(CQRS)事件存储。好主意?


219

虽然我已经遇到卡夫卡之前,我刚刚意识到卡夫卡也许可以作为(基础)一CQRSeventstore

Kafka支持的主要观点之一:

  • 事件捕获/存储,当然都是HA。
  • Pub / sub架构
  • 重播事件日志的能力,使新订户可以在事件发生后向系统注册。

诚然,我不是100%精通CQRS /事件源,但是这似乎与事件存储区应该的非常接近。有趣的是:关于Kafka用作事件存储,我实在找不到太多,所以也许我错过了一些东西。

那么,卡夫卡缺少什么东西可以使其成为一个好的活动库?能行吗?用它生产吗?对洞察力,链接等感兴趣

基本上,系统状态是根据系统曾经收到的事务/事件保存的,而不仅仅是保存系统的当前状态/快照(通常这样做)。(将其视为会计中的总帐:最终所有事务都加到最终状态)。这允许进行各种有趣的事情,但请仔细阅读提供的链接。


嗨,吉尔特·詹。回顾一下,您如何处理这个问题?我有一个相关的问题(在此处公开:stackoverflow.com/questions/58763727/…)。大多数人认为,Kafka的采用似乎依赖于附加日志的不可移植性,高吞吐量和分区顺序保证。我看到与主题内快速搜索有关的问题(用于实体“重构”),无事务原子性和跨分区无排序(100%的订单保证意味着仅使用1个分区杀死并发)
tony _008

最后没有说服它,因为我结束了那个项目。因此,没有个清楚的答案,我很害怕
Geert-Jan

Answers:


119

Kafka旨在成为一个消息传递系统,它与事件存储库有很多相似之处,但是引述了它们的介绍:

Kafka群集将所有已发布的消息(无论是否已被使用)保留一段可配置的时间。例如,如果将保留时间设置为两天,则在发布消息后的两天内,该消息可供使用,之后将被丢弃以释放空间。Kafka的性能相对于数据大小实际上是恒定的,因此保留大量数据不是问题。

因此,尽管可以无限期地保留消息,但是期望消息将被删除。这并不意味着您不能将其用作事件存储,但是最好使用其他东西。看看EventStore作为替代方案。

更新

Kafka文档

事件源是应用程序设计的一种样式,其中状态更改以时间顺序记录记录。Kafka对大量已存储日志数据的支持使其成为以这种风格构建的应用程序的绝佳后端。

更新2

使用Kafka进行事件采购的一个关注点是所需主题的数量。通常,在事件源中,每个实体(例如用户,产品等)都有事件流(主题)。这样,可以通过重新应用流中的所有事件来重构实体的当前状态。每个Kafka主题都包含一个或多个分区,每个分区都存储为文件系统上的目录。随着znode数量的增加,ZooKeeper也将带来压力。


16
我当时正在看卡夫卡,还有一个担心:我没有注意到关于乐观并发的任何事情。理想情况下,我可以说:“仅当对象的最新事件仍为N时,才将此事件添加为N + 1项。”
达里安2014年

2
@Darien:我可能正在使用Redis喂Kafka的设置(使用Redis Notifications)。由于Redis允许乐观并发(使用Watch / multi-exec),因此应该可以工作
Geert-Jan

2
@Darien我不是事件源专家,但是我的理解是,一般而言,您不需要乐观的并发性,因为事件根据定义是对已经发生过的事情的记录。
约翰

4
@John我认为,如果您已经对非冲突事件进行了权威排序,则意味着它们存在的地方是您实际的事件存储技术,而Kafka只是用作分发它们的辅助系统。
达里安

1
这里也有重要的信息:groups.google.com/forum
#!topic/dddcqrs/rm02iCfffUY

283

我是Kafka的原始作者之一。Kafka可以很好地用作事件源的日志。它具有容错能力,可以扩展到巨大的数据大小,并且具有内置的分区模型。

我们在LinkedIn上将其用于此表单的多个用例。例如,我们的开源流处理系统Apache Samza 内置了对事件源的支持

我想您对使用Kafka进行事件源知之甚少,主要是因为事件源术语在Kafka最流行的消费者网络领域似乎并不普遍。

我已经写了一些关于这种风格卡夫卡的使用这里


2
将要发布该链接:)很棒的博客文章。能够发表评论会很好,因为我有很多问题。@ Geert-Jan还研究了“ Lambda体系结构”,这非常相似,并且名称由Storm作者给出,主要在许多示例中使用基于hadoop的事件日志
Sebastien Lorber 2014年

6
@Jay:既然我对这个话题重新产生了兴趣,您能否详细说明一下Kafka 似乎旨在使它的已发布消息在一定时间后过期的事实?如果使用Kafka作为事件源,则消息应无限期存储。它可能是可配置的,但这会带来问题吗?
Geert-Jan

2
kafka和eventstore之间有什么比较吗?特别是,我喜欢在事件存储中称为Projections的FRP上的关注。在卡夫卡/萨姆扎有类似的东西吗?
CMCDragonkai 2014年

4
我也对@ Geert-Jan给Jay的问题感兴趣。Kafka不适合实际的事件源交易方,因为每个域合计(想想数百万个)需要一个事件流(主题)。但是,它非常适合将事件从例如GetEventStore馈入其中。但这仅适用于无限保留的事件(在我们的案例中),除了一些简短的评论之外,这似乎不是Kafka支持的用例吗?我在这里弄错了吗?例如,Samza假定只有两种情况:基于时间的保留或基于密钥的保留。还有其他..
Stephen Drew

3
@eulerfx假设我们想将Kafka用作事件源系统的存储,应该如何实现乐观锁定/并发?
Krzysztof Branicki

51

我继续回到此质量检查。而且我发现现有答案不够细致,因此我添加了这个答案。

TL; DR。是或否,这取决于您的事件源使用情况。

我知道有两种主要的事件源系统。

下游事件处理器=是

在这种系统中,事件发生在现实世界中,并记录为事实。例如用于跟踪产品托盘的仓库系统。基本上没有冲突事件。一切都已经发生,即使它是错误的。(即,托盘123456放在卡车A上,但原定在卡车B上。)然后,通过报告机制检查事实是否存在异常。Kafka似乎非常适合这种下游事件处理应用程序。

在这种情况下,可以理解为什么卡夫卡人提倡将其作为事件采购解决方案。因为它与单击流中已使用的方法非常相似。但是,使用术语“事件源”(而不是“流处理”)的人可能是指第二种用法...

应用程序控制的真理来源=否

由于用户请求通过业务逻辑传递,这种应用程序声明了自己的事件。Kafka在这种情况下不能很好地工作,主要有两个原因。

缺乏实体隔离

这种情况需要能够为特定实体加载事件流。这样做的常见原因是为业务逻辑建立一个瞬态写入模型,以用于处理请求。在Kafka中这样做是不切实际的。使用每个实体的主题可能会允许这样做,但是当可能有成千上万个实体时,这不是入门。这是由于Kafka / Zookeeper中的技术限制所致。

以这种方式使用瞬态写入模型的主要原因之一是使业务逻辑更改便宜且易于部署。

建议不要为Kafka使用每个类型的主题,但这会要求为该类型的每个实体加载事件,只是为了获取单个实体的事件。由于您无法通过日志位置分辨哪些事件属于哪个实体。即使使用快照从已知的日志位置开始,这也可能是大量需要处理的事件。

缺乏冲突检测

其次,由于对同一实体的并发请求,用户可以创建竞争条件。保存冲突事件并在事件发生后解决它们可能是非常不希望的。因此,重要的是能够防止发生冲突的事件。为了扩展请求负载,通常使用无状态服务,同时使用条件写入来防止写入冲突(仅当最后一个实体事件为#x时才进行写入)。又名乐观并发。Kafka不支持乐观并发。即使它在主题级别提供了支持,它也必须一直到实体级别才能生效。要使用Kafka并防止发生冲突的事件,您将需要在应用程序级别使用有状态的序列化编写器。这是一个重要的体系结构要求/限制。

更多信息


每条评论更新

该评论已被删除,但问题是这样的:那么人们将什么用于事件存储?

似乎大多数人都将他们自己的事件存储实现放在现有数据库之上。对于非分布式方案,例如内部后端或独立产品,如何创建基于SQL的事件存储的文档已备有证明。并且在各种数据库之上都有可用的库。还有EventStore,是为此目的而构建的。

在分布式方案中,我已经看到了几种不同的实现。Jet的Panther项目使用Azure CosmosDB和Change Feed功能来通知侦听器。我在AWS上听说过的另一个类似实现是使用DynamoDB及其Streams功能来通知侦听器。分区键可能应该是流ID,以实现最佳的数据分发(以减少过度配置的数量)。但是,在Dynamo中跨流进行完全重播非常昂贵(读取和费用合理)。因此,还为Dynamo Streams设置了此impl以将事件转储到S3。当新的侦听器上线或现有的侦听器要进行完整重播时,它将读取S3以先追赶。

我当前的项目是一个多租户场景,我在Postgres之上建立了自己的项目。像Citus之类的东西似乎适合于可伸缩性,即按帐篷+流进行分区。

Kafka在分布式方案中仍然非常有用。将每个服务的事件公开给其他服务是不平凡的问题。事件存储通常不是为此创建的,但这恰恰是Kafka做得很好的。每个服务都有其自己的内部事实来源(可能是事件存储或其他),但会收听Kafka以了解“外部”正在发生的事情。该服务还可以向Kafka发布事件,以将“有趣的事情”通知“外部”。


1
@Dominik我在“更新”部分(第二段)中提到了EventStore。我将返回并链接它。我已经尝试过了,它的性能令人印象深刻。对于我们的小型团队,暂时不引入其他数据库被认为更重要,因此Postgres(也用于视图)。我们有可能在将来或将来的产品中迁移到EventStore。
Kasey Speakman

2
@KaseySpeakman主题与分区不同。一个主题具有一个或多个分区。保证分区在任何给定时刻每组只有一个使用者。利用这种方式对实体进行分区。您不需要每个实体一个主题,甚至每个实体都不需要一个分区。您只需要对它们进行分区,以确保寻址到同一实体的所有命令都进入同一分区。
安德鲁·拉尔森

1
@KaseySpeakman许多实体可以共享一个分区。谁说您总是必须通过重播事件直接从事件存储中加载实体的状态?还有其他方法可以实现相同的概念,而不必严格遵循逐行Greg Young的实现。
安德鲁·拉尔森

1
@AndrewLarsson如果您不按实体划分分区,那么如何在实体级别上防止发生冲突的事件?既然我们已经完全解决了并发冲突,那么也许您应该在媒体上发表您自己的文章,或在生产中使用Kafka进行事件源(而非流处理)的方式发表文章。如何按类型进行分区并且没有实体级并发控制。我会读的,如果我不同意的话,我什至不会在评论中拖拉您。
Kasey Speakman,

2
@KaseySpeakman以任何方式使用Kafka都不容易。但是,如果您处于认真考虑CQRS和事件源的规模,那么您就处于无法负担以简单方式做事的范围。您的并发模型直接影响您的规模-不要随意选择一个。同样,HTTP也不是一种可靠的传输方式,同样,如果您的规模如此之大,您将无法花费时间来解决丢失和/或重复的消息问题。可以通过在客户端和命令处理器之间使用Kafka来解决所有问题,但是是的,这是以复杂性为代价的。
安德鲁·拉尔森

20

您可以将Kafka用作事件存储,但不建议这样做,尽管它看起来似乎是不错的选择:

  • Kafka仅保证至少交付一次,并且事件存储中有无法删除的重复项。 更新: 在这里您可以了解使用Kafka为何如此困难,以及有关如何最终实现此行为的一些最新消息:https : //www.confluent.io/blog/exactly-once-semantics-are-possible-heres-how -apache-kafka-does-it /
  • 由于不变性,当应用程序演变且需要转换事件时,无法操纵事件存储(当然,有诸如上投的方法,但...)。可能有人说过您永远不需要转换事件,但这不是正确的假设,有可能您备份了原始文件,但将它们升级到了最新版本。这是事件驱动架构中的有效要求。
  • 持久保存实体/聚合快照和重播的位置将变得越来越慢。从长期的角度来看,创建快照是事件存储的必需功能。
  • 给定Kafka分区是分布式的,与数据库相比,它们很难管理和备份。数据库更简单:-)

因此,在做出选择之前,请三思。将事件存储作为应用程序层接口(监视和管理),SQL / NoSQL存储以及作为代理的Kafka的组合,比让Kafka扮演两个角色来创建完整功能的完整解决方案更好。

事件存储是一项复杂的服务,如果您认真考虑将事件源,CQRS,Sagas和其他模式应用于事件驱动的体系结构并保持高性能,那么它需要的不仅仅是Kafka所能提供的。

随时挑战我的答案!您可能不喜欢我所说的关于您最喜欢的具有很多重叠功能的经纪人的说法,但是,Kafka并不是作为事件存储而设计的,而是更多地作为高性能经纪人和缓冲区来同时处理快速生产者和缓慢消费者的情况,例如。

请查看eventuate.io微服务开源框架,以了解有关潜在问题的更多信息:http : //eventuate.io/

截至2018年2月8日更新

我不会在评论中加入新信息,但会在某些方面达成共识。此更新更多有关微服务事件驱动平台的一些建议。如果您对微服务的鲁棒性设计和可能的最高性能感到认真,那么我将为您提供一些您可能不感兴趣的提示。

  1. 不要使用Spring-很棒(我经常使用它),但同时又又笨又慢。它根本不是微服务平台。它只是一个框架,可以帮助您实现一个框架(此背后的大量工作。)。其他框架是“仅仅是”轻量级的REST或JPA或侧重点不同的框架。我建议最好使用同类最佳的开源完整微服务平台,该平台可追溯到纯Java根:https : //github.com/networknt

如果您想了解性能,可以将自己与现有的基准套件进行比较。 https://github.com/networknt/microservices-framework-benchmark

  1. 完全不要使用Kafka :-))这是个半开玩笑。我的意思是,尽管卡夫卡很棒,但它是另一个以经纪人为中心的系统。我认为未来将出现在无代理消息系统中。您可能会感到惊讶,但是比Kafka系统快得多:-),当然,您必须降到较低的水平。看编年史。

  2. 对于事件存储,我建议使用称为TimescaleDB的高级Postgresql扩展,该扩展专注于大量的高性能时间序列数据处理(事件是时间序列)。当然,CQRS,事件源(重播等功能)是内置在light4j框架中的,该框架使用Postgres作为低存储空间。

  3. 对于消息传递,请尝试查看编年史队列,地图,引擎,网络。我的意思是摆脱这种老式的以经纪人为中心的解决方案,而使用微消息系统(嵌入式消息传递系统)。编年史队列实际上比Kafka还要快。但是我同意这不是一站式解决方案,您需要进行一些开发,否则您就可以购买企业版(付费版)。最后,通过消除维护Kafka集群的负担,可以从Chronicle上构建自己的消息传递层而付出的努力。


有趣的观点。愿意详细说明几点吗?> Kafka仅保证至少交付一次,并且事件存储中有无法删除的重复项。您似乎暗示有这样的事情,那就是一次交货。afaik(我对此非常确定)在分布式系统中没有这样的东西。2)关于你的观点2 :(事件来源/ dddd)的经典流派认为事件本质上是不变的。即:它们发生了,无法改变过去。实际用途是什么-回顾一下更改它们的情况?谢谢!
Geert-Jan

1.)Hazelcast,以确保每个消息将被处理一次且仅处理一次。2.)我不喜欢服务代码中的_V2之类的东西,因此您可以备份存档并将旧事件重新创建为新版本(您仍然拥有原始的事实),也可以直接在Event中隐藏/构建此功能。存储快照功能,因此可以单点上传->事件存储。您对此有什么解决方案?
肯赛

1)至少一次+幂等。即:检查事件是否已经看到。如果是这样,请跳过。或者更好的是,采取幂等的动作。当然,这并不总是可能的。2)我从未遇到过需要版本化事件的情况。我始终将事件本身视为真理的来源,并包括我将需要的所有信息。这样做,我从未遇到过需要不同事件结构和/或事件数据的情况。但是也许是ymmv。有兴趣了解在什么情况下您实际上需要更新事件。
Geert-Jan

1.)可以选择。.)2.)从一开始,您的数据结构就很完美了:-)幸运的你,哈哈。我可能在当前项目中不需要它,但是我正在eventuate.io的分支上构建了一个完整的平台,并结合了一些高性能的JEE方法,这些方法仅来自轻型最终4j。但如果你有兴趣深入探查我推荐这篇文章:leanpub.com/esversioning/read
剑圣

1
顺便说一下,Kafka现在只支持一次交货。更新项目符号1
OneCricketeer


0

是的,Kafka在事件源模型(特别是CQRS)中效果很好,但是您在设置主题的TTL时要格外小心,并始终牢记Kafka不是为此模型设计的,但是我们可以很好地使用它。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.