Pulsar官方文档翻译-概念和架构-架构概述(Architecture Overview)
官网原文标题《Concepts and Architecture--Architecture Overview》
翻译时间:2018-11-05
官网原文地址:http://pulsar.apache.org/docs/en/concepts-architecture-overview/
译者:本文整体讲解了Pulsar的架构,涵盖Pulsar实例的组成以及组件提供的特性。
------------------------------------------------------------------
架构概述
在最高层,Pulsar的实例由一个或者多个Pulsar集群组成。同一个实例内的集群间可以相互复制数据,
Pulsar集群由下面三部分组成:
- 一个或者多个broker负责处理和负载均源源不断从生产者发送出的消息,并将他们发送给消费者。它与配置存储交互来处理相应的任务,将消息存储在BookKeeper实例中(aka bookies)。它依赖ZooKeeper集群处理特定的任务,等等。
- 由一个或多个bookie组成的BookKeeper集群,处理消息的持久存储
- 用于集群操作的Zookeeper集群
下图是Pulsar集群的说明:
在更大的实例层面来看,实例范围的ZooKeeper集群,负责配置管理,处理多集群间的协调工作。例如,geo-replication。
Broker
Pulsar消息broker是无状态的组件,主要作用是运行另外两个组件:
- HTTP服务器,通过REST API来实现管理任务,以及为producer和consumer提供topic查询
- 调度器,异步TCP服务,通过客制化的binary protocol处理所有数据的传输
出于性能的考虑,消息通常被分发到托管ledger缓存,除非backlog已经超出了缓存的大小。如果backlog对于缓存来说增长的太大,broker将开始从BookKeepr读取数据条目。
最后,为了支持全局topic的geo-replication,broker使用复制器查询出发布在本区域尾部的条目,然后通过Java客户端类库重新把他们发步到远程地域上。
Pulsar broker的管理指南,请参考broker向导
集群
Pulsar的实例,由一到多个Pulsar集群组成。而集群组成如下:
- 一到多个Pulsar broker
- ZooKeeper仲裁,作为集群层面的配置和协调
- 一套bookie,用作来消息的持久存储。
集群可以通过geo-replication相互复制。
Pulsar cluster的管理指南,请参考cluster向导
元数据存储
Pulsar使用Apache ZooKeeper存储元数据、集群配置数据、协同数据。在Pulsar实例中:
- 配置存储仲裁存储了租户的配置信息、namespace,以及其他所有需要全局一致的信息条目。
- 每个集群有他自己本地的一套ZooKeeper,存储了集群指定的配置和协调信息,如元数据的所有权、broker负载报告、BookKeeper ledger元数据等等。
持久存储
Pulsar为应用提供消息发送的保证。一旦消息成功到达Pulsar broker,他将会被发送到他预期的目标上。
这种保证,需要消息被发送及确认前,被持久存储。这种消息传输的模式,通常被称为持久化消息传输。在Pulsar中,所有消息的N份拷贝被存储及同步在硬盘上,例如,跨越两台server的4份拷贝,存储于镜像RAID卷。
Apcahe BookKeeper
Pulsar使用Apache BookKeeper系统来持久存储消息。BookKeeper是分布式 write-ahead log (WAL) 系统,为Pulsar提供了很重要的优势:
- 它使得Pulsar可以使用很多称为legder的独立log。随着时间的移动,可以为topic创建很多的ledger。
- 它为有序数据提供了非常高效的存储,并处理数据条目复制。
- 在各种可能的系统崩溃下,它可以保证ledger的读一致性。
- 它提供跨bookie的I/O分配。
- 它在容量及吞吐量上都能做到可扩展。可以通过为集群增加bookie立即提升容量。
- Bookie的设计支持处理成千上万ledger的读写并发。通过使用多个硬盘设备---一块用于journal,其他的用做普通存储,这把读操作从正在进行的写操作延迟影响中隔离出来。
对于消息数据,还有游标也被持久存储在BookKeeper。游标是消费者订阅的位置。BookKeeper使Pulsar可以用流行的可扩展方式存储consumer的位置。
在Pulsar仅支持消息持久存储的时候,所有的topic名称中都有persistent描述。下面是一个例子:
persistent://my-tenant/my-namespace/my-topic
Pulsar也支持临时(非持久)消息存储。
下图展示了broker和bookie是如何交互的:
Ledger
ledger是单写入仅可追加的数据结构,被分配给多个BookKeeper的存储节点或者bookie。Ledger条目被复制到多个bookie。Ledge自己的语义十分简单:
- Pulsar broker可以创建ledger,往ledger添加条目,关闭ledger。
- ledger关闭后--无论是主动还是因为写入程序错误---他都可以被以只读的方式打开。
- 最后,当ledger中的条目不再需要时,整个ledger都将从系统中被删除(跨所有bookie)
Ledger的读一致性
BookKeeper最主要的长处是他在存在失败的情况下,能够保证ledger的读一致性。因为ledger仅可被单线程写入,这让程序很轻易就做到高效增加条目,而不需要考虑一致性。失败后,ledger将会执行恢复程序,最终确定ledger状态并发布最后提交的条目日志。经过上述操作,ledger的所有读取者会被保证读取到完全一样的内容。、
托管ledger
考虑到BookKeeper Ledger提供单一log抽象,构建于ledger之上的类库,被称为托管ledger,它代表了单一log的存储层。托管ledger代表消息流的抽象。它有一个单独的写入者,往流的末尾持续添加数据,多个游标在消费流数据,每个游标都持有自己的关联位置。
在内部,一个单独的托管ledger使用多个BookKeeper ledger来存储数据。使用多个ledger出于以下两个原因考虑:
- 失败后,ledger不再可写,一个新的ledger需要被创建
- 当ledger含有的全部消息被所有cursor都消费完成时,ledger就可以被删除了。这允许ledger周期回滚。
Journal存储
BookKeeper中,journal文件包含了BookKeeper的事务日志。在对ledger更新前,bookie需要确认描述更新的事务已经被持久保存。一旦bookie开启或者老的journal文件达到了journal文件大小的阈值(通过journalMaxSizeMB 配置),新的journal文件将被创建。
Pulsar代理
直接连接到Pulsar的broker上,是Pulsar客户端和pulsar集群交互的一个方法。然而在某些情况下,这种直连方式不但不可行也是不被希望的,因为客户端并没有直连broker的地址。如果你在云环境下运行Pulsar或者Kubernetes或者类似的平台,客户端直连broker看上去并不可行。
Pulsar代理对此问题提供了一个方案,通过作为集群中所有broker的单一网关。如果你运行Pulsar代理(可选),所有Pulsar集群的客户端连接都将通过代理,而不是和broker直接通信。
出于性能和容错的考虑,你可以运行你需要的的Pulsar代理实例数量
关于架构,Pulsar代理从ZooKeeper获取它所需要的所有信息。当你在机器上启动代理,你需要提供ZooKeeper的连接串来指定集群以及实例范围的配置存储集群。示例如下:
$ bin/pulsar proxy \
--zookeeper-servers zk-0,zk-1,zk-2 \
--configuration-store-servers zk-0,zk-1,zk-2
Publsar代理文档
Pulsar代理的相关使用文档,请参考Pulsar proxy admin documentation.
关于Pulsar代理需要知道的要点:
- 连接的客户端不需要提供任何指定配置来使用Pulsar代理。除了更新服务URL的IP,你并不需要为现有程序更新客户端配置(例如你正在通过Pulsar代理运行负载均衡)。
- Pulsar代理支持TLS encryption 和 authentication
服务发现
连接到Pulsar broker的客户端需要能通过单个URL和整个Pulsar实例通信。Pulsar提供了内置的服务发现机制,你可以按照部署Pulsar实例指南中的说明来构建此机制。
你可以按你的需要来使用自己的服务发现系统。如果你使用自己的系统,仅需要做一件事:当客户端执行一个HTTP请求,例如http://pulsar.us-west.example.com:8080,客户端需要被重定向到目标集群某个活跃的broker上,无论通过DNS,HTTP还是IP重定向,或者其他的手段。
下图说明了Pulsar的服务发现:
此图中,Pulsar集群通过DNS名称pulsar-cluster.acme.com被寻址。例如在Python客户端中,可以通过如下方式进入这个Pulsar集群:
from pulsar import Client
client = Client('pulsar://pulsar-cluster.acme.com:6650')