目录

序言

2006年,我在英国的ThoughtWorks公司工作。那时的办公室里充满了一种独特的活力,各种有趣的事情正在发生。敏捷(Agile)运动正如火如荼,BDD(行为驱动开发)蓬勃发展,人们开始尝试“事件溯源”(Event Sourcing);同时,SOA(面向服务的架构)也被引入到规模较小的项目中,旨在解决我们在大型系统实施过程中遇到的一些问题。

我参与的一个项目由Dave Farley领导。他精力充沛、性格开朗,这种乐观而充满活力的特质感染了他所参与的每一项工作。该项目是一个相当标准的中型企业级应用,包含一个Web门户,客户可以通过它申请各类房产过户相关服务。系统随后会启动一系列同步或异步流程,将客户请求的各项服务付诸实施。

这个项目中有不少值得关注的地方,但最令我印象深刻的,是各项服务之间的通信方式。这是我参与过的第一个完全基于“事件协作”构建的系统。在此之前,我接触过其他几种基于服务的系统,它们大多采用RPC(远程过程调用)或“请求-响应”式消息传递机制;相比之下,这个系统给我的感觉截然不同。将新服务直接接入事件流的过程显得格外灵动;而通过查看事件日志,观察系统运行的“叙事”过程飞速流转,更让人感到一种深层的满足感。

几年后,我在一家大型金融机构工作。当时,公司希望构建一个核心数据服务平台,作为整个企业的枢纽,让各类应用程序能够从中获取支撑银行运作的关键数据集——例如交易记录、估值信息、参考数据等。我发现这类问题非常有吸引力:它不仅在技术上极具挑战性,而且尽管此前已有不少银行和其他大型企业尝试过类似方案,但此时的技术发展水平已足以让我们构建出真正有趣且具有变革意义的系统。

然而,搞定技术问题仅仅是个开始。该系统需要与公司内部的各个主要部门对接,这意味着涉及大量的利益相关方,他们有着各自不同的需求、发布计划,以及对系统正常运行时间(uptime)的各种期望。我记得在一次为期两周的利益相关方启动会议上,我们讨论了系统的设计方案,并探讨了该项目在实际落地时所面临的种种挑战。这看起来是一项极其艰巨的任务——不仅在技术层面,在组织层面亦是如此——但从理论上讲,它确实是可行的。

于是,我们组建了一支团队,成员来自 ThoughtWorks、Google 以及其他一些机构。最终构建出的系统具备了一些相当有趣的特性:数据存储层将可查询的数据保存在内存中,并分布在每个数据中心内的 35 台机器上,从而能够应对来自计算网格(compute grid)的访问压力。写入操作直接通过查询层进入消息系统,而这个消息系统(在当时看来颇为罕见)成为了整个系统的“记录系统”(system of record,即数据事实的权威来源)。查询层和消息层均采用了分片(sharding)设计,以实现线性扩展。因此,每一次插入或更新操作,本质上也是一次事件发布;这种机制无法被绕过,因为它已被深深植入到整个架构的核心之中。

将消息系统作为“记录系统”的妙处在于,你可以对这一数据流进行多种用途的再开发:将其记录在文件系统上以便进行数据恢复;将其同步至其他数据中心;利用它来填充各类数据库,从而支持报表生成与数据分析;当然,还可以将其广播给任何通过 API 接入并希望获取这些数据的用户。

不过,在当时,我并未完全意识到将消息系统作为“记录系统”的真正深意。我记得曾在 QCon 大会上介绍过这个项目;当时,我原本重点讲述的是一套精巧的分布式连接(distributed join)层,但听众们却对幻灯片中那个我一带而过的“消息系统作为记录系统”的页面产生了浓厚兴趣,提出的问题反而比针对那套复杂连接层的问题还要多。随着时间的推移,人们逐渐发现,尽管该系统具备诸多特性——例如能加速连接查询(join)的数据驱动预缓存、基于文档的 SQL 接口、不可变数据模型以及延迟绑定的模式(schema)——但大多数客户的实际需求其实略有不同,且更为简单。起初,他们或许会直接使用该数据服务,但随着时间推移,出于各种需求,他们往往会选择将数据复制一份并独立存储,从而按自己的方式进行处理。尽管如此,他们依然觉得中央数据集很有价值,因此常会从中提取一个子集,日后再回来获取更多数据。回过头来看,对于这类场景,一个“针对存储数据集而优化”的消息系统,似乎比一个“针对发布数据集而优化”的数据库更为合适。不久之后,Confluent 公司成立了,而 Kafka 恰好成为了解决此类问题的理想方案。

这两个案例(房产过户应用与银行级数据服务)之所以耐人寻味,是因为它们之间的关联远比表面看起来更为紧密。房产过户应用展现出了极佳的协作性,且具备良好的可插拔特性;而在银行场景中,规模庞大的各类应用与服务通过事件进行集成,同时还能利用一套可供回溯与查询的历史参考数据。尽管两者所处的上下文截然不同——前者仅涉及单一应用,后者则涵盖了整个企业——但这两个系统之所以设计精妙,很大程度上得益于它们对“事件”的运用。

如今的流处理系统在诸多方面与上述案例已大不相同,但其底层的核心模式其实并未发生根本性的改变。当然,细节往往决定成败;在过去几年里,我们观察到客户在解决这类问题(以及其他诸多问题)时,采取了多种不同的方案。分布式日志与流处理工具恰好非常适合应对这些挑战。在这本小书中,我尝试提炼并总结了这些方案中的关键要素。

本书阅读指南

本书共分为五个部分。第一部分旨在奠定基础,通过相关章节介绍 Kafka 和流处理;即使是经验丰富的从业者,也能从中获得对核心概念的有效概览。第二部分探讨了如何构建事件驱动系统,阐述了此类系统与有状态流处理之间的关联,并介绍了“事件协作”(Event Collaboration)、“事件溯源”(Event Sourcing)和“命令查询职责分离”(CQRS)等模式。第三部分侧重于概念层面,在第二部分的基础上,将相关理念扩展至整个组织架构的维度。在此,我们将审视当今许多常见的做法,并深入探讨诸如“将事件流作为事实来源”(event streams as a source of truth)等模式。第四部分和第五部分则更具实践性。第五部分开始涉及少量代码;此外,书中还配套了一个 GitHub 项目,旨在帮助你利用 Kafka Streams 构建细粒度服务。

第一章的引言部分对本书涵盖的主要概念进行了宏观概述,是阅读本书的理想起点。

致谢

本书的完成离不开许多人的直接或间接贡献。我要特别感谢 Jay Kreps、Sam Newman、Edward Ribeiro、Gwen Shapira、Steve Counsell、Martin Kleppmann、Yeva Byzek、Dan Hanley、Tim Bergland,当然,还要感谢我那始终充满耐心的妻子 Emily。

在 GitHub 上查看本页或提交勘误 ↗