四年运维生产经验分享:Nordstrom的事件溯源系列之一


在Nordstrom,我们一直在探索一种特定类型的近实时数据流,称为事件溯源/事件采购,通过结合开源项目和过去四年运维生产的功能。在此过程中,我们学到了很多东西,并希望分享我们所看到的一些机会。

什么是事件溯源,我为什么要关心?
在事件源架构中,整个系统的单一事实来源是有序事件分类帐(通常也称为日志,日志,流(在运动时)或表(在静止时))记录现实世界的事件 、 观察或决定的一切。只使用事件分类帐可以确定性地恢复所有应用程序及其视图的应用程序状态。
事件记录事件发生时真实的事实,代表世界上发生的事物,并与业务相关并被其理解。例如:“周一下午3:16,SKU J012以200美元的价格售出给客户31337。”
消费应用程序从分类帐中读取并记录或处理数据。一个常见的用例是生成RESTful请求 - 响应API(如“Inventory”或“Catalog”)所面向的数据的特定于领域的只读聚合视图。
我们从事件源架构中看到的好处包括:

  • 提高数据质量:由于您使用与业务功能团队相同的事件进行分析,因此可大大提高机器学习和分析数据的质量。
  • 可重构的架构:您的技术设计更贴近现实世界中的技术设计,使您的技术架构更加一致,更易于理解您的业务团队(“当这个,然后那个”)。
  • 快速反应:近乎实时(约2秒)对系统中任何位置发生的任何事情做出反应,复杂性降低。
  • 可扩展性:可轻松扩展到每秒数十万个事件,并增加数百个事件使用者。
  • 业务连续性:让每个消费者按照自己的节奏从分类账中读取消除背压,并为保护下游系统和从停机中恢复创造一个自然的断路器机会。
  • 服务供应:强大的服务解耦缩短了新服务的上市时间或旧服务的更改,包括在实时生产数据上并行运行多个版本或多个完整服务。这使您可以轻松迁移到新服务并丢弃旧服务(“数据库为牛”)。
  • 技术不可知:事件不依赖于其源系统的任何技术细节,并且对业务人员来说是可以理解的,通过向任何团队提供完整的系统知识来增加自主性(“事件不知道它们来自大型机”)。
  • 历史记录:创建审核或重放历史记录的机会,允许重新创建历史状态以进行调试或灾难恢复。
  • 供应商不可知:通过在另一个服务提供商的云中复制事件分类账或者在预置位置上/下来减少供应商锁定的幽灵。
  • 设置一个高标准:每个额外的消费者通过提高生产者数据质量和新鲜度的要求来推动质量进入流,从而创造一个每个人都受益的良性循环。

你能给我一个事件源系统的例子吗?
当然!想想下棋。传统的数据库相当于正在进行的游戏的拍照 , 从某个时刻拍下的快照,你可以看到所有作品的位置和正在玩的人,你可以计算出谁赢得胜利的概率。将其与事件源的国际象棋系统进行对比,其中焦点在于将所有单独的移动记录到分类帐上(在国际象棋中,称为移动记录符号)。对于每次移动,我们在分类帐中看到一个条目,其中包含表格编号,游戏编号,移动作品的玩家姓名,移动的日期和时间以及移动的结果(例如,如果捕获了任何碎片) )。
由于可以访问所有动作的分类帐,您可以在游戏中的任何位置(事件源架构的黄金标准)完全恢复游戏状态。您还可以轻松地为您的系统添加功能,如国际象棋时钟,锦标赛经理,作弊检测,玩家分析,国际象棋教练,用于检测最有趣的游戏以进行评论或突出显示的系统等等。

事件源和无服务器架构
无服务器函数基本上是事件驱动的。触发无服务器函数有两种基本方法:直接调用,或间接,基于世界上发生的事情。第二种方法可用于在每次发生事件时将强大的事件序列链接在一​​起 - 您可能会想到一个(有用的)Rube Goldberg机器。
一旦您对事件触发的生态系统感到满意,就很自然地需要一种以可靠和强大的方式跟踪,维护和处理这些事件的方法。事件分类帐(无服务器处理)为您提供此功能。如果你以一种强大的方式使用无服务器系统,我预测你很可能很快会深入思考事件源架构(如果你还没有)。

事件溯源和物联网
在物联网系统中,传感器(如温度或光传感器)是事件生产者,而执行器(如灯或锁)是事件消费者。此外,这些传感器和执行器通常依赖于不可靠的网络连接。分类帐是处理偶尔连接(或离线优先)方案并确保系统保持一致的强大工具。许多物联网生态系统在幕后使用分类账来处理离线情景。CRDT(融合数据类型)是确保偶尔连接设备之间数据融合的另一个迷人方向,值得整篇博客发布。

我们来定义一些术语
我们将在本系列中使用许多技术术语,其中一些具有多个应用程序。我们来定义一些术语:

1. 分布式分类帐/统一日志/日志/流
特定事件的地方(例如:在此日期和时间,Rob以这种方式编辑文档。)按照收到的顺序写下来。分类帐仅附加(在最后写入新事件)和不可变(用“笔”写,永不改变)。它通常是分布式的,因为它被复制以获得持久性并进行分区以实现可伸缩性。

2.分类帐
为了划分处理事件流的工作,使用与称为分区键的每个事件相关联的特定属性将分类帐分成单独的分区(有时称为分片)。

3.分区键
事件中的派生或分配字段,用于标识最重要的排序事件的主题。(例如:如果客户名称/ ID用作分区键,则同一客户的所有操作将由所有事件使用者以一致的顺序处理。banq注:如果以DDD聚合设计,同一类聚合放入一个分区,因为聚合中的事件都必须以顺序排序,类似数据库一个库)

4. 碎片/分区
一个简单的真实世界分区示例可能是:由姓氏的第一个字母(例如:AG,HN,OU,VZ)拆分的老式的大学课程注册日事件中的多个物理表。每个表(以及通向它的行)相当于分区或分片。通过使用事件的键值作为分区,分区标记作为发出事件的位置。

5. 在分区键中排序保证
在每个分区键中,我们可以保证所有事件都是有序的(Rob做A,然后是B,然后是C)。但是我们不能保证跨越分区键的排序(在Rob做C之前,Grace先做过D吗?)。键之间的排序也是至关重要的,因为它允许我们让多个不同的读者在阅读分类帐的任何时候都得出相同的结论。这在消费者之间提供了分布式共识,而不需要他们彼此交谈。

6.事件发布者和消费者
分别来自流的事件的发送者和接收者。或者在物联网环境中,系统的传感器(温度,光检测器,按钮等)和执行器(电动机,显示器,灯等)。另一种替代命名/思维方式是现实的观察者和解释者。

7.事件使用者的偏移/分类帐位置
消费者在流的一个或多个分片中读取的位置。它表示消费者成功处理流的程度以及应该在何处恢复处理。

8.物化视图/只读聚合/直写高速缓存
当消费者从事件流中读取并创建有状态对象(如NoSQL数据库表)时,它正在创建事件流的“物化视图”。这方面的一个例子就是我们上面讨论过的棋盘图片。所有的游戏移动都依据分类账更新,并且每个快照图片成为系统的一个物化视图。单独的物化视图可以是整数数组,表示每个玩家的国际象棋时钟剩余秒数。

9. 逻辑时钟
逻辑时钟指的是读取器在分类帐中的位置,就其读取位置从开头的偏移量而言。这是一种相对的因果时间感。“通过分类帐位置2718,我们为慈善机构筹集了1,000美元。”使用任意当前物理时间概念的挂钟时间表达是:“根据我的观察,下午12:15我们为慈善机构筹集了1000美元。”这两者是不同的,使用逻辑时钟时间对于分类账是确定性的,因为许多原因,挂钟时间可能会有问题。(时区,漂移,闰秒,时钟调整导致及时跳过)

10. 矢量时钟/向量时钟
向量时钟指的是读取器跨多个分类帐的位置(这些分类帐可以是单个主题的多个分区/分片)。Slack中的一个示例 -:您在所订阅的所有频道中读取的消息偏移集合形成了您已消费内容的矢量时钟。在此示例中,使用向量时钟(以及其他内容)来确定您是否有未读消息。(banq注:从客户端角度看)

11. 最终的一致性
如果当您结束了所有的修改后,所有参与者都会得到相同的结果,那么系统最终会保持一致。例如,当我在信用卡上收取一些费用时,银行余额不会立即更新,但我相信它最终会更新。这与强烈的一致性相反,每个参与者总是会有相同的结果。