最近在阅读《Mastering Apache Pulsar》一书,与《Apache Pulsar In Action》一书相比,这本书最大的特点就是图片多,每个知识点,每个晦涩的理论,都用图片展示,一图胜千言,的确如此!
到目前为止,我们已经讨论了使用Apache Pulsar系统的动机,Pulsar创建的历史背景,以及一些使用Pulsar为系统提供动力的公司。现在,我们有了足够的背景,是时候查看Pulsar整体结构,并探索其组成部分了,更重要的是,为什么它们会一起工作。我们先来看看Pulsar的每个组件(见图4-1):即Pulsar broker、Apache BookKeeper和Apache ZooKeeper。然后,我们将了解在这三个项目中使用的一种标准技术:Java编程语言和Java虚拟机。
图4-1 Pulsar的组件包括node、Apache BookKeeper和Apache ZooKeeper。
4.1 Brokers正如前面提到的,Pulsar的模块化允许系统分离其职责,并选择最好的技术来处理每一个任务。Pulsar的职责之一是提供一个接口,让发布者和订阅者可以连接到它。
Pulsar brokers(代理)将处理这些任务以及以下任务(见图4-2):
topic数据存储的临时管理
与Apache BookKeeper和ZooKeeper通信
模式(schema)验证
broker之间的交互
Pulsar 函数和Pulsar IO运行时环境
图4-2 Pulsar节点在Java虚拟机上有一个Java底层实现。Pulsar函数和Pulsar IO也是用Java实现的。Pulsar支持多个HTTP和TCP点在集群内进行通信。
让我们仔细看看Pulsar brokers。
4.1.1 消息缓存Pulsar代理是无状态的,因为它们不会在消息生命周期中使用的Pulsar代理磁盘上存储任何数据。在这种方法中,Pulsar在消息代理中是独立的,因为大多数类似的系统都以某种方式将消息的存储和检索耦合在一起。无状态这个特点,既有优点也有缺点。缺点是需要另一个系统来进行状态管理,并且需要一些抽象来将Pulsar的存储需求转换为存储系统。这样做的好处是,存储需求与计算需求是分开的,从而产生了更容错的存储层。
如果Pulsar代理负责在代理上存储主题的状态,那么就会出现许多关于如何在代理上存储数据以及如何处理失败场景的问题。
由于我们的旅程才刚刚开始,让我们保持简单,探索以下三个考虑因素:
存储数据
向集群中添加新节点
从集群中移除节点
在第3章中,我们从低容量系统的存储和检索的角度讨论了在分布式系统中存储数据的方式。当涉及到数据如何跨节点分布以及像丢失一个节点这样的事件如何影响整个系统时,大容量系统甚至需要考虑更多的事情。Pulsar没有考虑理解存储问题的复杂性,而是选择依赖Apache BookKeeper进行存储,并使用代理作为存储的无状态协调器。
Pulsar在BookKeeper之上使用了一个抽象概念,称为托管账本(managed ledger)。managed ledger是Pulsar broker需要存储的消息与BookKeeper中的ledgers 之间的桥梁(将在本章后面讨论)。可以将ledgers看作BookKeeper中最高的存储抽象。managed ledger是一个API,用于跟踪ledger的大小和状态,以及何时开始一个新的ledger。
图4-3是Pulsar主题的典型拓扑。Broker1负责主题读写。对于读操作,它写入主题集合(ensenble)中的所有BookKeeper实例(主服务器,或bookies);对于读取操作,它从该ledger的leader请求数据。managed ledger管理该接口。这是否意味着每写一次,Pulsar broker都必须从bookies检索数据?不完全是。Pulsar代理(broker)有一个managed ledger缓存,允许一些消息被缓存到broker上供消费者使用。
图4-3 在这个Apache Pulsar集群中, bookies存储来自主题的数据。
在流上下文中,每个消息都需要写入BookKeeper。Broker不是直接写到Bookkeeper,然后在消费者请求时再从BookKeeper中读取,而是将最新的消息直接发送非消费者。这避免了与BookKeeper之间的往返,如图4-4所示。
图4-4 Pulsar Broker可以直接跟踪活跃消费者的最新事件。
重要的是要记住,即使managed ledger可以为订阅主题的消费者缓存消息,缓存只是一个缓存(见图4-5)。缓存是短暂的,很容易创建和销毁。它们不应该是永久的数据存储,因为存储在缓存中的数据是一种方便,但也是一个潜在的头痛问题。幸运的是,Pulsar broker缓存数据的范围是有限的。在第5章和第6章中,您将了解Pulsar的消息生命周期。
图4-5 managed ledger缓存是由Pulsar broker维护的可配置缓存。它存储了一个存储在BookKeeper中的数据ledger,并维护一个接口来写入BookKeeper。
4.1.2 BookKeeper和ZooKeeper通信正如本章开始所讨论的,Pulsar节点与BookKeeper和ZooKeeper一起工作,作为消息平台的架构。Pulsar brokers需要与ZooKeeper和BookKeeper通信,以进行主题管理和其他配置值。如何以及何时进行这种通信完全由Pulsar brokers管理。我们有必要花些时间来更好地理解broker与BookKeeper和ZooKeeper之间的通信。
ZooKeeper中存储了所有与Pulsar集群相关的元数据。包括关于哪个broker是主题的leader的元数据、服务发现的配置值以及其他管理数据。存储在ZooKeeper中的大部分数据都缓存在Pulsar节点上,并且有一个配置驱动的生命周期,即何时从ZooKeeper中提取新数据。与ZooKeeper通信是Pulsar生命周期的一部分。
正如前面所讨论的,BookKeeper是Pulsar中的存储引擎。所有的信息数据都存储在Pulsar中。从Pulsar存储和检索的每个消息都需要与BookKeeper通信。BookKeeper的通信接口将在第12章中详细介绍。
4.1.3 模式验证模式验证是确保发布到Pulsar主题的新消息遵循预定义形状的过程。为了确保消息遵循模式,Pulsar代理与Pulsar模式注册中心一起执行验证。模式验证的生命周期将在第6章中讨论; 然而,确保模式的责任是重要的,并且完全落在brokers身上,因此我们将在这里简要讨论它。
brokers以两种关键方式处理模式验证。首先,它们拥有与主题相关的模式的所有权。brokers回答以下问题:
这个主题是否有与之关联的模式?
与主题相关的模式是什么?
该模式是否要求新消息遵循该模式?
此外,brokers可以确保对发送中的消息进行验证。模式验证是端到端消息传递系统的重要组成部分,而Pulsar brokers服务于此目的。
4.1.4 broker之间的交互如前所述,brokers负责特定主题的读写。客户端可能向不负责该主题的brokers请求数据。在这种情况下会发生什么? 图4-6描述了这一点。每个broker使用存储在ZooKeeper中的元数据来确定它是否是leader(负责topic的leader),如果不是leader,那么leader是谁。brokers可以将客户端路由到正确的brokers,以开始发布(或检索)消息。
图4-6 Broker 1不是Topic A的leader,因此将生产者重定向到正确的Broker。
4.1.5 Pulsar函数和Pulsar IO在本节的开始,我强调了模块化在Pulsar设计中的重要性。在后面的部分中,您了解了brokers的责任有多大。你可能会想到Pulsar的设计可以更加模块化。在考虑模块化时,务必记住两件事。首先,减少brokers的责任,把他们放到其他地方,有意义吗? 第二,就可靠性和可扩展性而言,将这些责任转移到其他地方是否一定会改善Pulsar? 一般来说,这两个问题的答案都是否定的。这个规则的例外是Pulsar IO和Pulsar函数。
Pulsar作为一个项目提供了一些简单的方法来使用基本的Pulsar brokers以及扩展,如Pulsar函数和Pulsar IO。你可以使用Pulsar函数或Pulsar IO为新的Pulsar用例,而不增加额外的开销或困难。这种便利的限制因素是:brokers是Pulsar中吞吐量的主要来源。一个集群每秒可以接收多少消息在很大程度上受brokers的可用性影响。如果brokers忙于处理Pulsar Functions或Pulsar IO任务,将影响整个系统的性能。 场景
在许多情况下,这种性能下降不会有问题,但为了足够的规模,将您的Pulsar Functions或Pulsar IO移动到另一个集群将是一个改进。幸运的是,Pulsar正好提供了一种机制。
4.2 Apache BookKeeperApache BookKeeper是一种通用的数据存储系统。与Pulsar和ZooKeeper一样,BookKeeper也是由雅虎开发的。在2010年达到了以下要求:
写和读延迟< 5 ms
持久、一致和容错的数据存储
在数据写入时读取数据
提供实时和长期存储接口
BookKeeper是一个雄心勃勃的项目,其目标是为存储构建原语,这些原语可以应用于大量的项目和未来很长一段时间的增长。BookKeeper是用Java编写的,并且大量使用了Apache ZooKeeper(我们将在本章后面讨论)。BookKeeper的架构如图4-7所示。主服务器被称为bookies,它们可以被编排为一个集群(ZooKeeper也可以这样编排)。博bookies包含一个称为ledger的底层存储系统。
图4-7 Bookies是存储数据的服务器(在ledger上)。Apache ZooKeeper (ZK)负责管理服务发现和Bookies之间的协作。
如何构建具有Apache BookKeeper所承诺的性能需求和持久性的系统?从高级别的需求来看:
用于存储数据的简单语义
跨节点分布数据存储的容错方式
从任何单个节点故障中恢复的简单方法
对于第一个需求,Apache BookKeeper实现了一个append-only的日志,称为ledger。ledger由称为entries的任意数据组成。ledger的序列称为流(见图4-8)。
图4-8 BookKeeper存储的高级视图。stream是ledgers的集合,ledgers是由更小的entries组成的。
使用Apache BookKeeper Java客户机创建entries和ledgers也很简单。BookKeeper有两个Java API: BookKeeper Ledger API和Advanced Ledger API。BookKeeper Ledger API是较低级别的,专注于用户直接与ledgers交互。Advanced Ledger API提供了一些额外的特性,为用户提供了更细粒度的仲裁配置控制(稍后将介绍),以及BookKeeper事务安全方面。出于我们的目的,我们将使用Ledger API做一些事情,以说明直接与BookKeeper交互可能会是什么样子。
我们将执行以下操作:
创建一个新的BookKeeper客户端
创建一个ledger
向ledger写入entries
关闭ledger
重开ledger
读取所有的entries



