消息队列比较

2025-11-02T08:29:03.png2025-11-02T08:29:03.png

2025-11-02T08:31:07.png2025-11-02T08:31:07.png

维度kafkapulsarrocketMq
延迟队列不支持秒级延迟消息:所有延迟投递的消息会被 Delayed Message Tracker 记录对应的 index,consumer 在消费时,会先去 Delayed Message Tracker 检查,是否有到期需要投递的消息,如果有到期的消息,则从 Tracker 中拿出对应的 index,找到对应的消息进行消费,如果没有到期的消息,则直接消费正常的消息。对于长时间的延迟消息,会被存储在磁盘中,当快到延迟间隔时才被加载到内存里时间轮
死信队列不支持,通过offset记录消费位置重试topic和死信topicDLQ记录消费失败的消息
消息确认发送方确认机制 ack=0,不管消息是否成功写入分区 ack=1,消息成功写入首领分区后,返回成功 ack=all,消息成功写入所有分区后,返回成功与 Kafka 类似也会提交 Offset,区别在于消费者对于消费失败的消息,可以标记为消息消费失败,Broker 会重试投递,如果累计多次消费失败,会投递到死信队列。使用专门的 Cursor 管理,累积确认和 Kafka 效果一样;提供单条或选择性确认。
TTLKafka 根据设置的保留期来删除消息。有可能消息没被消费,过期后被删除。不支持 TTL。Pulsar 支持 TTL,如果消息未在配置的 TTL 时间段内被任何消费者使用,则消息将自动标记为已确认看接口似乎是支持的
消息顺序性分区内的消息有序独占订阅的流模式只保证了消息的顺序性,共享订阅队列模型不保证有序性需要用到锁来保证一个队列同时只有一个消费者线程进行消费,保证消息的有序性
消费模式subscribe 方式:当主题分区数量变化或者 consumer 数量变化时,会进行 rebalance;注册 rebalance 监听器,可以手动管理 offset 不注册监听器,kafka 自动管理。assign 方式:手动将 consumer 与 partition 进行对应,kafka 不会进行 rebanlance。Exclusive 独占模式(默认模式):一个 Subscription 只能与一个 Consumer 关联,只有这个 Consumer 可以接收到 Topic 的全部消息,如果该 Consumer 出现故障了就会停止消费。灾备模式(Failover):当存在多个 consumer 时,将会按字典顺序排序,第一个 consumer 被初始化为唯一接受消息的消费者。当第一个 consumer 断开时,所有的消息(未被确认和后续进入的)将会被分发给队列中的下一个 consumer。共享模式(Shared):消息通过 round robin 轮询机制(也可以自定义)分发给不同的消费者,并且每个消息仅会被分发给一个消费者。当消费者断开连接,所有被发送给他,但没有被确认的消息将被重新安排,分发给其它存活的消费者。KEY 共享模式(Key_Shared):当存在多个 consumer 时,将根据消息的 key 进行分发,key 相同的消息只会被分发到同一个消费者。广播消费指的是:一条消息被多个 consumer 消费,即使这些 consumer 属于同一个 ConsumerGroup,消息也会被 ConsumerGroup 中的每个 Consumer 都消费一次,广播消费中 ConsumerGroup 概念可以认为在消息划分方面无意义。集群消费模式:一个 ConsumerGroup 中的 Consumer 实例平均分摊消费消息。例如某个 Topic 有 9 条消息,其中一个 ConsumerGroup 有 3 个实例(可能是 3 个进程,或者 3 台机器),那么每个实例只消费其中部分,消费完的消息不能被其他实例消费。
可靠性request.required.acks=-1 (全量同步确认,强可靠性保证)request.required.acks=1(leader 确认收到,默认)request.required.acks=0 (不确认,但是吞吐量大)跟 Kafka 类似的概念,叫 Ack Quorum Size(Qa),Qa 是每次写请求发送完毕后需要回复确认的 Bookie 的个数,其数值越大则需要确认写成功的时间越长,其值上限是副本数 Qw。为了一致性,Qa 应该是:(Qw+1)/2 或者更,即为了确保数据安全性,Qa 下限是 (Qw+1)/2跟 Kafka 类似
负载均衡一个 broker 通常就是一台服务器节点。对于同一个 Topic 的不同分区,Kafka 会尽力将这些分区分布到不同的 Broker 服务器上,zookeeper 保存了 broker、主题和分区的元数据信息。分区首领会处理来自客户端的生产请求,kafka 分区首领会被分配到不同的 broker 服务器上,让不同的 broker 服务器共同分担任务。每一个 broker 都缓存了元数据信息,客户端可以从任意一个 broker 获取元数据信息并缓存起来,根据元数据信息知道要往哪里发送请求。kafka 的消费者组订阅同一个 topic,会尽可能地使得每一个消费者分配到相同数量的分区,分摊负载。当消费者加入或者退出消费者组的时候,还会触发再均衡,为每一个消费者重新分配分区,分摊负载。kafka 的负载均衡大部分是自动完成的,分区的创建也是 kafka 完成的,隐藏了很多细节,避免了繁琐的配置和人为疏忽造成的负载问题。发送端由 topic 和 key 来决定消息发往哪个分区,如果 key 为 null,那么会使用轮询算法将消息均衡地发送到同一个 topic 的不同分区中。如果 key 不为 null,那么会根据 key 的 hashcode 取模计算出要发往的分区。 一个 broker 通常是一个服务器节点,broker 分为 master 和 slave,master 和 slave 存储的数据一样,slave 从 master 同步数据。nameserver 与每个集群成员保持心跳,保存着 Topic-Broker 路由信息,同一个 topic 的队列会分布在不同的服务器上。发送消息通过轮询队列的方式发送,每个队列接收平均的消息量。发送消息指定 topic、tags、keys,无法指定投递到哪个队列(没有意义,集群消费和广播消费跟消息存放在哪个队列没有关系)。tags 选填,类似于 Gmail 为每封邮件设置的标签,方便服务器过滤使用。目前只支 持每个消息设置一个 tag,所以也可以类比为 Notify 的 MessageType 概念。keys 选填,代表这条消息的业务关键词,服务器会根据 keys 创建哈希索引,设置后, 可以在 Console 系统根据 Topic、Keys 来查询消息,由于是哈希索引,请尽可能 保证 key 唯一,例如订单号,商品 Id 等。rocketmq 的负载均衡策略规定:Consumer 数量应该小于等于 Queue 数量,如果 Consumer 超过 Queue 数量,那么多余的 Consumer 将不能消费消息。这一点和 kafka 是一致的,rocketmq 会尽可能地为每一个 Consumer 分配相同数量的队列,分摊负载。
集群方式天然的‘Leader-Slave’无状态集群,每台服务器既是 Master 也是 Slave。分区首领均匀地分布在不同的 kafka 服务器上,分区副本也均匀地分布在不同的 kafka 服务器上,所以每一台 kafka 服务器既含有分区首领,同时又含有分区副本,每一台 kafka 服务器是某一台 kafka 服务器的 Slave,同时也是某一台 kafka 服务器的 leader。kafka 的集群依赖于 zookeeper,zookeeper 支持热扩展,所有的 broker、消费者、分区都可以动态加入移除,而无需关闭服务,与不依靠 zookeeper 集群的 mq 相比,这是最大的优势。存算分离常用 多对'Master-Slave' 模式,开源版本需手动切换 Slave 变成 MasterName Server 是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。Broker 部署相对复杂,Broker 分为 Master 与 Slave,一个 Master 可以对应多个 Slave,但是一个 Slave 只能对应一个 Master,Master 与 Slave 的对应关系通过指定相同的 BrokerName,不同的 BrokerId 来定义,BrokerId 为 0 表示 Master,非 0 表示 Slave。Master 也可以部署多个。每个 Broker 与 Name Server 集群中的所有节点建立长连接,定时注册 Topic 信息到所有 Name Server。Producer 与 Name Server 集群中的其中一个节点(随机选择)建立长连接,定期从 Name Server 取 Topic 路由信息,并向提供 Topic 服务的 Master 建立长连接,且定时向 Master 发送心跳。Producer 完全无状态,可集群部署。Consumer 与 Name Server 集群中的其中一个节点(随机选择)建立长连接,定期从 Name Server 取 Topic 路由信息,并向提供 Topic 服务的 Master、Slave 建立长连接,且定时向 Master、Slave 发送心跳。Consumer 既可以从 Master 订阅消息,也可以从 Slave 订阅消息,订阅规则由 Broker 配置决定。客户端先找到 NameServer, 然后通过 NameServer 再找到 Broker。一个 topic 有多个队列,这些队列会均匀地分布在不同的 broker 服务器上。rocketmq 队列的概念和 kafka 的分区概念是基本一致的,kafka 同一个 topic 的分区尽可能地分布在不同的 broker 上,分区副本也会分布在不同的 broker 上。rocketmq 集群的 slave 会从 master 拉取数据备份,master 分布在不同的 broker 上。
性能 在多数资源配置相同的情况中,Pulsar 的优势更加明显,可以用相同的资源实现比kafka更好的性能
扩容方式Kafka 集群由于主题分区是物理存储在 Broker 节点上的,新加入的集群的节点并没有存储分区分片,也就无法提供马上提供服务,因此需要把一些 Topic 的分区分配到新加入的节点里,这里会涉及到一个分区数据均衡的过程,将某些分区的数据复制到新节点上。这个过程跟分区当前堆积的数据量、Broker 性能有关,有可能会出现由于源 Broker 负载过高,堆积数据过大,导致数据均衡的时间变长。Pulsar 的无限分布式日志以分片为中心,借助扩展日志存储(通过 Apache BookKeeper)实现,内置分层存储支持,因此分片可以均匀地分布在存储节点上。由于与任一给定 topic 相关的数据都不会与特定存储节点进行捆绑,因此很容易替换存储节点或缩扩容。另外,集群中最小或最慢的节点也不会成为存储或带宽的短板。RocketMQ 新节点直接加入到集群中,在新的 broker 创建新 topic 并且分配队列,或者在已有 topic 基础上分配队列。与 Kafka 的区别是,Kafka 的分区是在不同的物理机器上,而 Rocketmq 是逻辑分区,用的队列形式,因此不存在出现数据不均衡的情况。
事务消息都支持跨topic/partition事务生产者事务: 只支持生产消息的事务特性,即一批消息要不全部发送成功,要不全部发送失败。可以多个生产者设置同一个事务 ID ,从而把多个 Topic 、多个 Partition 放在一个事务中,实现原子性写入。事务回查: 不支持事务回查Exactly-Once: 端到端需配合幂等 + 事务无法处理“本地 DB 操作 + 发消息”的原子性,除非将 DB 操作也纳入流处理(如 Kafka Streams)。Producer 事务 + Transactional Reader(消费+生产): 允许事件流应用将消费、处理、生产消息整个过程定义为一个原子操作。可见,Pulsar 的事务消息可以覆盖消息流整个过程。事务回查: 可以通过Transaction Coordinator回查事务Exactly-Once: 支持生产者事务: Half Message 先写入,Commit 后才可见事务回查: 核心机制,Broker 定时回查(如果 Broker 未收到 Commit/Rollback,会主动回调 Producer 的 checkLocalTransaction 方法,查询本地事务状态。)Exactly-Once: 事务加幂等消费解决“本地 DB + 发消息”原子性问题

其他

Pulsar事务消息:Pulsar事务消息
消息堆积:消息堆积

添加新评论