Apache Pulsar是一个企业级的分布式消息系统。项目于2015年由 Yahoo 开源,2017年6月提交给 Apache 孵化器,2018年9月成为Apache的顶级项目。
目录
特性
架构概述
Broker
集群
元数据
持久存储
Apache BookKeeper
Pulsar相关概念
Topic
数据分区
命名空间
订阅类型
消费模式
生产环境实践
总结
Pulsar之所以能够称为下一代消息队列,主要是因为以下特性:
在最高层,Pulsar实例由一个或多个Pulsar集群组成。同一个实例内的集群间可以相互复制数据,
Pulsar集群由三部分组成:
下图是Pulsar集群的说明:
在Kafka和RocketMQ中,Broker负责消息数据的存储以及consumer消费位移的存储等,而Pulsar中的broker和他们两个有所不同。
Pulsar中的broker是一个无状态的节点,主要负责运行另外的两个组件:
消息通常被分发到托管ledger缓存,除非backlog已经超出了缓存的大小。如果backlog对于缓存来说增长的太大,broker将开始从BookKeepr读取数据条目。
为了支持全局Topic异地复制,Broker使用复制器查询出发布在本区域尾部的条目,然后通过Java客户端类库重新把他们发步到其他远程可用区。
Pulsar的实例,由一到多个Pulsar集群组成。而集群组成如下:
集群可以通过geo-replication相互复制。
Pulsar中的元数据主要存储到ZK中,在Pulsar实例中:
Pulsar为应用提供消息发送的保证。一旦消息成功到达Pulsar broker,他将会被发送到他预期的目标上。
这种保证,需要消息被发送及确认前,被持久存储。这种消息传输的模式,通常被称为持久化消息传输。在Pulsar中,所有消息的N份拷贝被存储及同步在硬盘上,例如,跨越两台server的4份拷贝,存储于镜像RAID卷。
Pulsar broker 在收到消息并进行确认之后,就必须确保消息在任何情况下都不会丢失。与其他消息系统不同的是,Pulsar 使用Apache BookKeeper来保证持久性。BookKeeper 提供了低延迟的持久化存储。Pulsar 在收到消息之后,将消息发送给多个 BookKeeper 节点(具体由复制系数来定),节点将数据写入预写式日志(write ahead log),同时在内存里也保存一份。节点在对消息进行确认之前,强制将日志写入到持久化的存储上,因此即使出现电力故障,数据也不会丢失。因为 Pulsar broker 将数据发给了多个节点,所以只会在大多数节点(quorum)确认写入成功之后它才会将确认消息发给生产者。Pulsar 就是通过这种方式来保证即使在出现了硬件故障、网络故障或其他故障的情况下仍然能够保证数据不丢失。
Apache BookKeeper是分布式 write-ahead log(WAL) 系统。
Pulsar使用BookKeeper系统来持久存储消息,BookKeeper为Pulsar提供了很重要的优势:
BookKeeper是一个可横向扩展的、错误容忍的、低延迟的分布式存储服务,BookKeeper中最基本的单位是记录,实际上就一个字节数组,而记录的数组称之为ledger,BK会将记录复制到多个bookies,存储ledger的节点叫做bookies,从而获得更高的可用性和错误容忍性。从设计阶段BK就考虑到了各种故障,Bookies可以宕机、丢数据、脏数据,但是主要整个集群中有足够的Bookies服务的行为就是正确的。
在Pulsar中,每个分区Topic是由若干个ledger组成的,而ledger是一个append-only的数据结构,只允许单个writer,ledger中的每条记录会被复制到多个bookies中,一个ledger被关闭后(例如broker宕机了或者达到了一定的大小)就只支持读取,而当ledger中的数据不再需要的时候(例如所有的消费者都已经消费了这个ledger中的消息)就会被删除。
Bookkeeper的主要优势在于它可以保证在出现故障时在ledger的读取一致性。因为ledger只能被同时被一个writer写入,因为没有竞争,BK可以更高效的实现写入。在Broker宕机后重启时,Pulsar会启动一个恢复的操作,从ZK中读取最后一个写入的Ledger并读取最后一个已提交的记录,然后所有的消费者也都被保证能看到同样的内容。
我们知道Kafka在0.8版本之前是将消费进度存储到ZK中的,但是ZK本质上基于单个日志的中心服务,简单来讲,ZK的性能不会随着你增加更多的节点而线性增加,会只会相反减少,因为更多的节点意味着需要将日志同步到更多的节点,性能也会随之下降,因此QPS也会受单机性能影响,因此0.8版本之后就将消费进度存储到了Kafka的Topic中,而RocketMQ最初的版本也类似,有几种不同的实现例如ZK、数据库等,目前版本采用的是存储到本机文件系统中,而Pulsar采用了和Kafka类似的思想,Pulsar将消费进度也存储到了BK的ledger中。
Ledger
ledger是单写入仅可追加的数据结构,被分配给多个BookKeeper的存储节点或者bookie。Ledger条目被复制到多个bookie。Ledge自己的语义十分简单:
Ledger的读一致性
BookKeeper最主要的长处是他在存在失败的情况下,能够保证ledger的读一致性。因为ledger仅可被单线程写入,这让程序很轻易就做到高效增加条目,而不需要考虑一致性。失败后,ledger将会执行恢复程序,最终确定ledger状态并发布最后提交的条目日志。经过上述操作,ledger的所有读取者会被保证读取到完全一样的内容。
托管ledger
考虑到BookKeeper Ledger提供单一log抽象,构建于ledger之上的类库,被称为托管ledger,它代表了单一log的存储层。托管ledger代表消息流的抽象。它有一个单独的写入者,往流的末尾持续添加数据,多个游标在消费流数据,每个游标都持有自己的关联位置。
在内部,一个单独的托管ledger使用多个BookKeeper ledger来存储数据。使用多个ledger出于以下两个原因考虑:
向 Pulsar 发送数据的应用程序叫作生产者(producer),而从 Pulsar 读取数据的应用程序叫作消费者(consumer)。有时候消费者也被叫作订阅者。发布订阅系统中最核心的概念是Topic,简单来说,Topic可以理解为一个管道,Producer可以往这个管道丢消息,Consumer可以从这个管道的另一端读取消息,但是这里可以有多个Consumer同时从这个管道读取消息。
写入主题的数据可能只有几个 MB,也有可能是几个 TB。所以,在某些情况下主题的吞吐量很低,有时候又很高,完全取决于消费者的数量。那么碰到有些主题吞吐量很高而有些又很低的情况该怎么处理?为了解决这个问题,Pulsar 将一个主题的数据分布到多台机器上,也就是所谓的分区。
在处理海量数据时,为了保证高吞吐量,分区是一种很常见的手段。默认情况下,Pulsar 的主题是不进行分区的,但通过命令行工具或 API 可以很容易地创建分区主题,并指定分区的数量。
在创建好分区主题之后,Pulsar 可以自动对数据进行分区,不会影响到生产者和消费者。也就是说,一个应用程序向一个主题写入数据,对主题分区之后,不需要修改应用程序的代码。分区只是一个运维操作,应用程序不需要关心分区是如何进行的。
主题的分区操作由一个叫作 broker 的进程来处理,Pulsar 集群里的每个节点都会运行自己的 broker。
每个Topic可以划分为多个分区,同一个Topic下的不同分区所包含的消息都是不同的。每个消息在被添加到一个分区后都会分配一个唯一的offset,在同一个分区内消息是有序的,因此客户端可以根据比如说用户ID进行一个哈希取模从而使得整个用户的消息都发往整个分区,从而一定程度上避免race condition的问题。
通过分区,将大量的消息分散到不同的节点处理从而获得高吞吐。默认情况下,Pulsar的topic都是非分区的,但是支持通过cli或者接口创建一定分区数目的Topic。
默认情况下Pulsar会自动均衡Producer和Consumer,但有时候客户端想要根据自己的业务规则也进行路由,Pulsar默认支持以下几种规则:
构建 Pulsar 的目的是为了支持多租户(multi-tenant)应用场景。Pulsar 的多租户机制包含了两种资源:资产(property)和命名空间(namespace)。资产代表系统里的租户。假设有一个 Pulsar 集群用于支持多个应用程序(就像 Yahoo 那样),集群里的每个资产可以代表一个组织的团队、一个核心的功能或一个产品线。一个资产可以包含多个命名空间,一个命名空间可以包含任意个主题。
Pulsar 各个组件间的关系:
命名空间是 Pulsar 最基本的管理单元。在命名空间层面,我们可以设置权限、调整复制选项、管理跨级群的数据复制、控制消息的过期时间或执行其他关键任务。命名空间里的主题会继承命名空间的配置,所以我们可以一次性对同一个命名空间内的所有主题进行配置。命名空间可以分为两种:
虽然本地命名空间和全局命名空间的作用域不同,但它们都可以在不同的团队或不同的组织内共享。如果应用程序获得了命名空间的写入权限,就可以往该命名空间内的所有主题写入数据。如果写入的主题不存在,就会创建该主题。
每个命名空间可以包含一到多个主题,每个主题可以有多个订阅者,每个订阅者可以接收所有发布到该主题的消息。为了给应用程序提供更大的灵活性,Pulsar 提供了三种订阅类型,它们可以共存在同一个主题上:
下图展示了这三种类型的订阅。Pulsar 的订阅机制解耦了消息的生产者和消费者,在不增加复杂性和开发工作量的情况下为应用程序提供了更大的弹性。
消费决定了消息具体是如何被分发到消费者的,Pulsar支持几种不同的消费模式: exclusive、shared、failover。
Pulsar 目前在助力 Yahoo 的主要应用,如Yahoo Mail、Yahoo Finance、Yahoo Sports、Gemini广告平台和 Yahoo 分布式键值存储系统Sherpa。很多场景都要求很强的持久性保证,比如零数据丢失,同时又要求很高的性能。Pulsar 从 2015 年开始部署到生产环境,现在在 Yahoo 的生产环境里大规模地运行。
Pulsar作为下一代分布式消息队列,拥有非常多吸引人的特性,也弥补了一些其他竞品的短板,例如地域复制、多租户、扩展性、读写隔离等等。
官网Pulsar架构概述
官网Pulsar消息系统概述
Pulsar VS. Kafka: 统一的消息消费模型(Queue + Stream)