Apache Pulsar 框架简介
一、架构概述
【1】Pulsar租户架构图
【2】Pulsar集群架构
【3】Pulsar关系概念图
【4】Pulsar订阅模式图
二、订阅模式介绍
【1】四种订阅模式
【2】Producer
【3】Consumer
【4】Topic
【5】多Topic订阅
【6】数据分区
【7】路由模式
【8】消息存留、过期、去重
三、对比Kafka
四、Bug修改 :端口号冲突
具 体 内 容
一、Pulsar框架介绍
Pulsar 是一个用于服务器到服务器的消息系统,具有多租户、高性能等优势,最初由 Yahoo 开发,目前由 Apache 软件基金会管理。
- 跨地域复制(geo-replication),单个实例原生支持多个集群(跨集群复制);
- 支持多种topic订阅模式:独占订阅、共享订阅、故障转移订阅、键共享;
- 通过Apache BookKeeper提供的持久化消息存储机制保证消息传递;
【1】Pulsar关系架构图:
-
property/tenant:一个property代表一个租户,一个property可包含多个namesapce;假设部署了一个Pulsar集群来支持多个应用程序,在企业中每个property都可以代表一个团队,一个核心的功能,或者一个产品线;
-
namespace:是Pulsar的基本管理单元,在namaspace级别可设置权限permission,备份fine-tune,跨集群管理消息数据的地理复制geo-replication、消息TTL等;一个namaspace里的所有topic都继承相同的设置;
-
topic:一种通道,用作从producer到consumer传输消息:持久(默认,硬盘)和非持久(仅内存);
【2】Pulsar集群架构及术语
Pulsar Cluster~= Broker 集群 + Bookkeeper +ZooKeeper
集群间可以通过跨地域复制(Geo-Replication)进行消息同步
- 多个Broker节点组成一个Pulsar Cluster;多个Pulsar Cluster组成一个Pulsar Instance。
- Pulsar通过geo-replication支持一个Instance内在不同的集群发送和消费消息。
- BookKeeper 有状态集群,负责消息数据的持久化存储(Bookie);
- Broker 集群属于无状态集群,只处理业务逻辑;
- ZooKeeper负责各种与配置和协调相关的任务(localZK负责cluster内部配置,globalZK负责cluster之间的配置);
- Global replication负责集群间的数据复制;
【3】Pulsar关系概念图
【4】Pulsar订阅模式图
Producer发布消息到topic,Consumer可订阅这些topic,处理发布过来的消息,在处理完成后发送确认。
(一旦订阅被创建,所有的消息都将被Pulsar保留,即使consumer断开连接。 只有在consumer确认消息被成功处理后,保留下来的消息才会被丢弃。)
消息队列:
- Producer是如何生产消息,发送到对应的Broker???
- Broker是如何处理消息,将高效的持久化以及查询???
- Consumer是如何进行消费消息???
二、订阅模式:producer-topic-subscription-consumer
Topic支持多种订阅模式: 独占(exclusive), 共享(shared)和灾备(failover),key共享(key_shared);
【1】四种订阅模式:
-
【独占模式(默认)】:只能有一个Consumer消费topic的消息,超过一个Consumer会收到错误;
-
【灾备模式】:同一时刻只有一个有效的Consumer,其余的Consumer作为备用节点,在Master Consumer不可用后进行替代。
-
【共享模式】:可以同时存在多个Consumer,每个Consumer处理Topic中一部消息。消息通过轮询机制分发给不同的消费者,并且每个消息仅会被分发给一个消费者。当某个消费者断开连接,所有被发送给它但没有被确认的消息将被重新安排,分发给其它可用的消费者(有两点需注意,1、不保证消息顺序; 2、不能使用累计确认);
-
【Key-shared模式】: 类似于shared模式,但是相同key的消息会传递给同一个消费者,(该模式限制:消息必须指定key/orderingKey;不能使用累计确认;该模式目前是测试版,可禁用);
【2】Producer:
- 发送模式:同步或异步
- 压缩:消息在发送中可压缩来节省带宽;
- 批处理:生产者将在单个请求中发送批量消息
- 分块(批处理需禁用):消息分块发送至broker(一P对一C、多P对一C)
【3】Consumer:
- 接收模式:同步或异步
- 监听:客户端库为consumers提供listener的实现,(例如Java客户端,提供MesssageListener接口,实现该接口,一旦接受到新的消息,received方法将被调用。
- 确认: 当consumer 成功消费掉一条消息后,会发送一个确认请求到broker,broker会丢弃这条消息,否则保存这条消息。单条确认 或 累计确认(共享模式不支持);
- 取消确认:当consumer 在一定时间内没有成功消费消息,想再次消费该消息,这个consumer就可以发送一个否定确认到broker,然后broker重发这条消息。(独占消费模式和灾备订阅模式中,消费者仅仅只能对收到的最后一条消息进行取消确认)。单条取消确认+累积取消模式;
- 确认超时:未确认消息会自动重新交付
【4】Topic:
- 持久topic(默认):
Pulsar保存所有没有确认的消息到多个BookKeeper的bookies中,持久性topic上的消息数据可以在 broker
重启和订阅者故障转移之后继续存在。 - 非持久topic:消息数据仅存活在内存。 如果broker挂掉或者因其他情况不能从内存取到,消息数据就可能丢失。
- 死信(Dead letter)topic:死信topic使您能够在消费者无法成功消费某些消息时消费新消息。在这种机制中,无法成功消费的消息存储在单独的topic,称为死信topic---仅仅适用于共享模式;
【5】多topic订阅(不能保证顺序性):
- 正则表达式(所有的主题必须在同一个namespace);
- 明确指定topic列表;
【6】数据分区
通常一个topic仅被一个broker服务,这限制了topic的最大吞吐量。 分区topic是特殊的topic类型,他可以被多个broker处理,这让topic有更高的吞吐量。分区的topic通过N个内部topic实现,N是分区的数量。 当向分区的topic发送消息,每条消息被路由到其中一个broker。 Pulsar自动处理跨broker的分区分布。
Topic1有5个分区(P0到P4),分布在3个broker上。因为分区数量多于broker数量,其中有两个broker每个处理两个分区,第三个broker则只处理一个。
Topic1的消息被广播给两个consumer,路由模式决定哪个broker处理哪个partition,订阅模式决定哪条消息发送到哪个consumer;
【7】路由模式:消息应该发布到哪个分区
- roundRobinPartition(默认):message无key则轮询,有key则hash(key)指定分区
- SinglePartition:无key,producer将会随机选择一个分区,把所有的消息发往该分区。
如果为message指定了key,分区的producer会把key做hash,然后分配消息到指定的分区。 - CustomPartition:使用客制化消息路由实现,可以决定特定的消息进入指定的分区。
【8】消息存留/到期/去重(namesapce级别管理)
消息的顺序与路由模式和消息的key有关;
- 按key分区:相同key的消息有序,并被发送到相同的partition(非客户自定义路由)
- 按producer:来自相同producer的消息有序,(singlepartition) Broker默认:
Broker默认:
- 立即删除所有已被consumer确认过的消息;
- 以消息backlog的形式,持久保存所有未被确认的消息;
存留:可以保存被consumer确认过的消息;
过期:可以给未被确认的消息设置TTL
去重:pulsar保证消息仅持久化一次(避免生产者幂)
三、与Kafka的不同
1、Pulsar是流式处理(Kafka)和队列的合体;
2、Pulsar存储计算分离,其他MQ不是;
3、Pulsar的broker是无状态的,而Kafka是有状态的;
4、Pulsar简单的跨域赋值、扩容简单,数据处理快;
四、Bug修改 :端口号冲突
1、Pulsar本地启动时出现端口冲突问题
Apache.Pulsar.zookeeper.BindException in ininating zookeeper: Address already in use:0.0.0.0:2181
问题:pulsar中自带的zookeeper客户端端口号被占用导致启动失败
解决方案一:杀掉占用该端口的其他进程
netstat -anp | grep 2181
kill -9 pid
解决方案二:更改pulsar中的conf文件修改端口号
grep 2181 *conf
vi *conf
:n 编辑下一个文档
:N 编辑上一个文档
:e# 编辑上一个文档,用于在文件间相互交换编辑时使用;
?# 编辑前一次编辑的文档