Apache Pulsar 的访问模式与分层存储

原作者:Ivan Kelly
翻译:StreamNative——Sijia

之前我们谈到了 Apache Pulsar 如何利用 BookKeeper 多副本的工作方式以及 BookKeeper 中不同的 I/O 模式。本文将讨论在 Pulsar 中多副本怎样与不同的 I/O 模式交互,以及 Pulsar 如何通过这种交互实现分层存储等。从本质上看,Pulsar 采用分层架构,而这种分层架构使得每种 I/O 模式都可以独立工作,因此读写之间永远不会相互干扰。分层还简化了以与 Pulsar 完全集成的方式添加存储层的操作,从而在对使用 Pulsar 的开发者不产生任何影响的条件下,降低增加存储层的成本,并提高新增存储层的可扩展性。

Pulsar 是一个提供发布-订阅和排队语义的消息系统。客户端可以是 producer 或 consumer,也可以是两者组合。生产客户端向 broker 发送消息,消费客户端从 broker 消费消息。Pulsar 将消息整理存放在 topic 中,并把 topic 分配给 broker。在一个 topic 内,Pulsar 保证全序原子广播(Total Order Atomic Broadcast),也就是说,一旦 Pulsar broker 向 producer ack topic 中某消息的发布,相对于同一 topic 中的其他消息,此消息将永远不会丢失、被复制或被重新排序。并且,消息顺序完全相同,所有 consumer 读取消息的顺序也完全相同。

Apache Pulsar 的访问模式与分层存储

Pulsar 中消息流程图解

Pulsar 使用 BookKeeper 作为 topic 积压消息的后备存储。Pulsar broker 作为 BookKeeper 存储顶部的无状态服务层。当 producer 向 Pulsar 发送消息时,Pulsar 立刻将此消息写入 BookKeeper。一旦 BookKeeper ack 写操作,broker 便可以向 producer ack 消息发布,并且 consumer 可以读消息。

消息系统中通常有三种 I/O 模式。

  • :发布消息到消息系统;
  • 追尾读:写入后,立即发送消息给活跃订阅者 ;
  • 追赶读:新 consumer 想从最新消息之前的某个点开始读取,或现有 consumer 长时间离线后返回时,consumer 从日志后缀中读取大量消息以进行追赶。
    不同于大多数其他消息系统,Pulsar 中每种 I/O 模式之间相互隔离。

最有趣的 I/O 模式是写模式,所有其他模式都遵循该模式。当 Pulsar broker 想为某个 topic 持久化消息时,broker 会将此消息写入一组 BookKeeper 节点,这组节点就定义为该 topic 日志的写入 quorum。每个接收消息的 BookKeeper 节点都将此消息添加到节点的日志文件中,而节点的日志文件存放于专用磁盘上。当足够多的节点 ack 写入以满足日志的多副本(即 ack quorum)要求时,则认为写操作已提交,并已向 producer ack。从这一点开始,该消息不可变,并且该消息将会一直占用日志偏移量。其他消息都不可以占用这个偏移量,此消息也不可再更改。

可以利用消息的不可变性有效地服务于消息系统的其他 I/O 模式。在 BookKeeper 节点日志中写入消息是规范的,并且如果用户停在这里,仍然可以访问此消息。但是,这样做效率不高,因为每次读都需要扫描所有日志以查找所需消息,并且不能截断日志来释放磁盘空间。不过已提交消息的不可变性允许在多个位置缓存该消息,以有效地进行读操作。

Apache Pulsar 的访问模式与分层存储

Pulsar 中不同级别缓存概览

第一级缓存是 Pulsar broker,可用于追尾读。提交消息后,可以直接将消息发给所有与此 topic 相关的订阅者,而不必使用磁盘。

下一级缓存是 BookKeeper 节点上的 ledger 存储磁盘。将消息写入 BookKeeper 节点上的日志时,同时也写入到定期 flush 的 ledger 存储磁盘的内存缓冲区。BookKeeper 节点使用此磁盘提供读操作。在 Pulsar 中,从内存缓冲区读消息很少见。追尾 consumer 通常直接从 Pulsar 的缓存中读消息。追赶 consumer 通常请求很早之前的消息,因此这些消息一般不存储在内存缓冲区。Ledger 存储磁盘服务于追赶读。Ledger 存储磁盘采用的存储消息的格式不仅保证在同一 topic 上尽可能按顺序读取,还优化了在同一磁盘上存储多个不同 topic 的能力。由于 ledger 存储磁盘与日志磁盘相互隔离,读操作不会影响日志磁盘中按顺序写入的性能。

如果为 Pulsar 配置了“分层存储”,则最后一级缓存为长期存储。分层存储允许用户对 topic backlog 中的较旧部分采用更节约成本的存储形式。分层存储利用了消息的不可变性,但粒度更大,因为在长期存储中单独存储每条消息会很浪费空间。Pulsar topic 日志由分片组成,每个分片默认对应一个包含 50000 条消息的序列。活跃分片只有一个,活跃分片之前的分片将关闭。当分片关闭时,无法继续添加新消息。假定分片中的单条消息不可变,并且单条消息的偏移量不可变,则此分片不可变。因此可以复制不可变对象到想要的任何位置。

要在 Pulsar 中使用分层存储,用户必须使用基于时间或基于大小的策略来配置 topic 命名空间以卸载分片。当命名空间中的 topic 达到策略中定义的阈值时,Pulsar broker 将 topic 日志中最旧的分片复制到长期存储中,直到该 topic 低于策略阈值。经过一段时间后,Pulsar 从 BookKeeper 中删除原来的分片,以释放磁盘空间。

Pulsar 支持将 Amazon S3 和 S3 兼容的对象存储用于长期存储,也支持 Azure 存储,并且从 Pulsar 2.2.0 起支持谷歌云存储。