一项 Gartner 机构的研究表明,企业正将注意力转向事件驱动 IT。研究指出了到 2020 年 CIO 必须优先考虑的三个事项。Apache Kafka 作为一种被广为采用的事件流平台,其使用量的增长有力地支撑了上述声明。三家主要的云服务提供商(AWS、GCP 和Azure)都提供了自己的事件流处理服务,一些业界领袖也加入了 CloudEvents 规范。开源项目 Liftbridge 通过提供类似于 Kafka 的可扩展日志 API,实现了对 NATS 消息系统的扩展。
Liftbridge 是由资深技术人员 Tyler Treat 创立的一个项目。Treat 是 NATS 引擎的一位长期贡献者,现在 NATS 已成为一个由 CNCF 负责的项目。在宣布项目开源的博客帖子中,Treat 指出该项目的目标是“填补基于日志的复杂消息系统(例如Apache Kafka 和 Apache Pulsar )与更为简单的云原生系统之间的空白”。为了解该项目的更多细节,并了解不断变化中的系统连接技术环境,InfoQ 采访了 Treat。
InfoQ:该项目意在解决哪些问题?
Tyler Treat:从根本上说,Liftbridge 要解决的问题,也正是 Apache Kafka 等系统要解决的同一类问题。Liftbridge 本质上是一个消息传递系统,它支持数据生成者与消费者的分离。和 Kafka 一样,Liftbridge 也是一个消息日志系统。不同于 RabbitMQ 之类的传统消息代理,Liftbridge 将消息添加到不可变的提交日志中。在传统的消息代理中,消息通常会被消费者从消息队列中取走。而使用 Liftbridge,消息在消费之后会依然时会保留在日志中,并支持多个消费者从同一日志中读取消息。它几乎等同于将数据库中的预写日志功能提取出来,并以服务的形式公开提供。这使得通过系统发送的消息可以重播(replay)。
该方法已实现了大量事件源和流处理相关的实际用例。例如,我们可以通过日志聚合将所有应用和系统日志写入到 Liftbridge,并以后者作为一种缓冲区,实现将日志传输给 Splunk、Datadog、冷存储以及任何关注日志数据的后端。在我曾供职的一家企业中,该方法通过提供一种数据获取方式,可以同时评估多个日志记录提供者。Liftbridge 和 Kafka 等系统提供了非常强大的数据流水线。
然而,我们构建 Liftbridge 的目的并非在于替代 Kafka,其更主要的目标在于为 NATS 提供一种类似 Kafka 的语义。NATS 是一种即发即弃(Fire-and-Forget)的高性能消息传递系统,由于它在设计上是始终处于开启状态的,并且非常简单,因此常被视为一种用于分布式系统的“拨号音”。但这样做的缺点是 NATS 的功能非常有限。我们继续使用上面的类比,可将 Liftbridge 看成是 NATS 的“语音邮件”。一旦有消息发布到 NATS,但是对消息感兴趣的消费者是离线的,那么 Liftbridge 就会将消息记录到日志,确保消息依然是可传递的。这个比喻可能并不完美,希望大家能明白其中的意思。
InfoQ:该架构历经多年的构建,您能概述一下其中的关键体会吗?
Treat: 首先,我必须要向设计 Kafka 的 Confluent 人员脱帽致敬,因为我认为 Kafka 是此类架构的真正开创者。Liftbridge 直接受 Kafka 的启发。它实际上借用了一些相同的概念,特别是数据复制和提交日志相关的实现。
在我的职业生涯中,曾参与了多个消息传递系统的实现。同时,我也是 NATS 和 NATS Streaming 的核心提交者之一。Liftbridge 也从 NATS 中汲取了大量灵感。我非常感谢 NATS 的创建者 Derek Collison 及其他团队成员。其中,不少人都曾是另一种主要的消息传递系统 TIBCO 的前员工。
我学到的最重要的,就是简单性是一种被低估的特性。很多系统出错的原因。在于它们试图实现得过多。由于范围太过宽泛,因此系统会受到多种因素的影响,包括性能、可扩展性、可操作性等。传统的 ESB 就是一个很好的例子,通常它们很慢,或是不能扩展,或是非常难以操作或使用。
我学到的另一件事,就是简单性通常有助于提高性能。例如,Liftbridge 和 Kafka 使用简单的仅添加(append-only)日志。这种做法使我们可利用顺序磁盘访问和操作系统页面缓存等功能。不同于大多数的消息代理,Liftbridge 和 Kafka 并不追踪消费者的状态。这不仅简化了系统,而且提高了性能。
然而,竞争性目标和权衡总是存在的。如果在考虑性能的同时忽略容错性或可扩展性,很容易实现快速性能。同样,可扩展性和容错性与简单性之间存在着权衡。Kafka 使用 ZooKeeper 处理群集和故障转移,因此它并不是一个简单系统。有别于 Kafka,Liftbridge 在内部使用了 Raft 替代了 ZooKeeper 的角色。另一方面,复杂性现在只存在于代码库中,而系统往往更容易操作。
通常,“简单性”最终只是将系统的复杂性推动到其它地方。使用相对简单的系统 Kafka,很多复杂性被推到 ZooKeeper 和客户端。使用 ESB 时,客户端的语义可能非常简单,但这是因为复杂性已经推送到服务器端。服务器端很容易处理这种复杂性,但是在一些情况下,例如需要分布式以支持容错、维护一致性以保持数据正确、保持系统快速等,简单性统是非常难以实现的。许多消息代理无法同时满足上述领域中的一个或多个。
最后一点,容错是非常难以有效实现的。我的意思是,现有的复杂系统尤其难以添加聚类、数据复制等操作。这些问题是与架构密切相关的,必须从系统的一开始就做出仔细的考虑。熟悉我的人都知道我是 John Gall 的 Systemantics 的粉丝。有几个 Systemantics 原则适用于此,它们分别是“一个复杂的系统无疑是从一个可工作的简单系统发展而来的”、“一个从零开始设计的复杂系统永远不会工作,也无法通过修修补补使其工作。我们必须重新从一个简单系统开始做起”。我正在从零开始实现复制功能,并将其以补丁的形式添加到现有的复杂系统中,这项工作非常具有挑战性。就容错而言,我们不一定需要从一开始就实现容错,但需要我们具有一种可发展的愿景和设计。
InfoQ: 是否可以让部分流量继续“仅仅”使用 NATS,或者是否可以将单个实例专用于基于日志的事件处理?
Treat: Liftbridge 项目意在为 NATS 提供补充,而不是要取代 NATS,或是改变 NATS 的行为。这是其设计的关键所在,也是 Liftbridge 有别于 NATS Streaming 的一个方面。也许对 Liftbridge 来说,更好的比喻是作为 NATS 的速记员,它恪尽职守地记录下来所有要播放的信息。这意味着。NATS 的流量将会继续正常的运行,无需考虑 Liftbridge 在后台所做的任何记录保存。这不需要做任何特殊的配置,也无需更改任何代码。
InfoQ:您是否看到存在一些特有的场景,其中只适合使用 ESB、轻量级消息代理、内存事件处理器(如 NATS)或是基于日志的事件处理器?或者,您是否看到一些合并用例中使用了一到两种消息传递引擎?
Treat:有趣的是,一些较新的系统试图支持去很多上述类型乃至更多类型的用例。例如,Apache Pulsar 提供了一种“统一”解决方案。它既可以提供传统的队列语义(例如,RabbitMQ 所支持的语义),也可以提供类似于 Kafka 那样的数据流语义。我最近还看到 Pulsar 支持可进行流处理计算的函数,大体上类似于 Apache Storm,Flink 或是 AWS Lambda 之类的功能。NATS Streaming 具有类似的功能,只是功能有限,例如提供队列语义的队列组(queue group)。
Confluent 的员工会宣称 Kafka 能满足用户的所有需求。Kafka 可能更适用于实时数据处理,但是对于微服务解耦,Kakfa 需要做一些扩展。改变开发人员的思维方式至少会是一场艰苦的战斗。要让开发人员适应“发布者 - 订阅者”模式,需要做大量艰苦的工作。对于大多数开发人员而言,通过流处理构建复杂应用并非一种自然而然的做法。实际上,Kafka 不仅仅与 RabbitMQ、ActiveMQ 或 ESB 等产生竞争,它还与数据库进行竞争,与 Istio 这样的服务网格竞争,与 REST 竞争。这是因为 Confluent 正试图从根本上改变开发人员构建系统的方式,而这是一个很大的挑战。
Streamlio 的员工会宣称 Pulsar 能满足用户的所有需求。如果开发人员有可用的解决方案,那么为什么还要从根本上去重新构建系统,或是采用消息队列、流数据流水线及流处理框架? CIO 喜欢这样做。
我个人采用的架构方法是使用一些相对较小的可组合组件。理想情况下,这可以通过一些合理的努力实现。我并不太喜欢近乎无所不包的系统,我认为有意思之处在于这正是导致大部分 ESB 失宠的部分原因,也是我与 NATS 产生共鸣之处。NATS 是一种非常简单并精心设计的系统。
我认为存在着运行多种类型系统的用例。NATS 非常棒,因为它是一种非常轻量级的系统。我们实际上已在一家企业将其用做服务网格原型,并作为守护进程运行在托管的虚拟机上。我曾谈到过,很多人在物联网中使用 NATS,并将其运行在嵌入式设备上。一位曾与我交谈过的用户使用 NATS 来控制射击场的机器人,这颇具吸引力(也有点可怕!)。还有一个用例中,将 NATS 作为具有数十万个节点的集群的控制平面。Kafka 非常适合大规模的数据处理,但由于使用了 JVM 和 ZooKeeper,因此是重量级的。只要用户具有需要使用 Kafka 的工作负载以及可支持 Kafka 的资源(这也意味着,会有一项业务仅是提供 Kafka 服务),那么他们完全可以使用 Kafka。但我认为,更传统的消息队列也有其一席之地。如果要使用消息队列,我会倾向于更多地依靠 Amazon SQS 或 Google Cloud“发布者 - 订阅者”等托管服务。
InfoQ:在您(及社区)看来,在达到可用于生产环境前,Liftbridge 还需满足哪些条件?
Treat:的确还需要实现一些项目。首先,Liftbridge 需要支持 TLS。在我看来,加密是一件首先需考虑的事情。它实际上并非十分难以实现,更重要的是该特性需要经过更严格的测试,包括负载测试和更强的故障注入测试,以消除任何性能或可靠性上的问题。在正确性方面,Liftbridge 需要支持最小规模的同步副本集。该设计主要为了支持用户通过请求写入仲裁而保证数据的持久性。最后,我个人还希望能看到的一些功能,例如日志压缩(通过仅保留具有最新密钥的消息而压缩日志)、按时间戳和时间增量记录日志、改进检测和监视,以及支持在 Liftbridge 中嵌入运行 NATS 的方式。
InfoQ:要摆脱传统的 ESB 转而采用类似于 Liftbridge 的系统,用户应该怎么做?
Treat:对于使用更传统的消息传递中间件的用户,我通常会要求他们首先评估自己为什么需要队列。有些情况下队列是完全适用的,但更多情况下,用户过早地将队列引入到一个并非自身真正需要的系统中。对此,参见一个非常好的博客帖子“如何将一块巨石劈成两半?”。该帖子指出,对于短期任务,用户真正需要的是一种负载均衡器,因为队列不可避免地会以“满”或“空”这两种状态运行。如果队列以满状态运行,并且用户没有推出足够的工作,那么客户端就会产生等待,因为这是短期存活的任务。如果队列以空状态运行,那么它是以一种慢速的负载平衡器方式运行。实际上,NATS 完全匹配对负载均衡器的描述,因为它并非一种队列,而是一种路由器。对于长期存活的任务,用户需要的是数据库。因为二者在客户端之外的任务管理上(例如,结果处理、故障故障等)存在着很多细微的差别。
如果你仍然踌躇不前,那么你应看到的是,从 ESB 迁移到 Liftbridge 的过程可能并不像你所想象的那样令人生畏。首先,NATS 提供了一个连接器框架,简化了 NATS 和传统技术的桥接过程(谨记,Liftbridge 只是 NATS 的扩展,因此数据将通过 NATS)。该连接器支持用户将消息中间件中的数据直接传输给 NATS(及 Liftbridge)。如果用户使用了一些更复杂的 ESB 功能(例如,基于内容的路由),那么也可以使用连接器将这些功能映射到 NATS。其次,尽管 NATS 并不支持 ESB 提供的许多功能,但它确实支持通配符订阅(wildcard subscription)。这是一种广为使用的强大功能,尤其是在 AMQP 中,它有助于将复杂的路由方案映射到 NATS。不同于 NATS Streaming,作为 NATS 扩展的 Liftbridge 完全支持通配符。
活动推荐:
2023年9月3-5日,「QCon全球软件开发大会·北京站」 将在北京•富力万丽酒店举办。此次大会以「启航·AIGC软件工程变革」为主题,策划了大前端融合提效、大模型应用落地、面向 AI 的存储、AIGC 浪潮下的研发效能提升、LLMOps、异构算力、微服务架构治理、业务安全技术、构建未来软件的编程语言、FinOps 等近30个精彩专题。咨询购票可联系票务经理 18514549229(微信同手机号)。
评论