因为做IM的缘故, 需要使用Queue进行削谷填峰, 这里对比市面上不同的queue, 从不同角度观察Queue.

feature

对比 Kafka/RocketMQ/qmq/pulsar, 不考虑consumer/producer 客户端上的特性, 针对broker实现上对比

Type/feature kafka rocketmq qmq pulsar
delay message false true true design
consume mode group 广播/集群 广播/group 广播/group
ack mode acc acc acc/One acc/One
ordered message partition queue queue sub-topic
history msg local local backup offload
高可用 partition rebalance Master/Slave Master/Slave bookkeeper + broker leader base zk
transaction true true false false

从定位上来说, kafka是实时流, rocketmq、qmq、pulsar 是MQ, pulsar 比较特殊, 很多feature和常规的MQ不一样, 但是实现了feature的效果, 比如, 在 消息消费模式中, pulsar 本身支持 Shared/Exclusive/Failover, 其中, Shared 和 广播模式一致, 除此之外, pulsar还支持 sub topic 模式, 将topic路由成多个 sub topic, 结合 Exclusive/Failover, 实现 group 模式.

基本思想

  • kafka:
    • 提供topic、partition视图, 一个topic由多个partition组成
    • broker上, 每个partition对应一个存储文件, 每个partition独立分散在不同的broker上, 每个broker上有多个partition存储
    • producer通过partition策略, 指定消息分发给topic下具体的某个partition, 然后发送给partition的leader broker.
    • consumer消费的时候, 以consumer group的方式进行订阅, broker会先执行join流程, 早期是broker内部分配, v2的版本通过client计算, consumer获取订阅的partition 列表和相应的broker地址, 连接broker地址进行pull方式消息拉取
  • Rocketmq
    • 提供 topic、queue视图, 一个topic有多个queue组成
    • broker上, 所有的topic/queue都写入一个 commit log文件, 通过线程异步的从commit log文件读取构建每个queue的consumer queue文件 + time index 文件.
    • producer通过selector策略, 确定发送给topic的一个queue, 然后发送给queue的broker
    • consumer以consumer group方式进行订阅, 从name server获取topic的路由信息, 值得注意的是, 订阅关系queue的分配是在consumer端维护的, consumer获取到要消费的queue, consumer会连接到broker上进行消息的拉取
  • pulsar
    • 提供topic、subTopic视图, 一个topic由多个subTopic组成
    • broker上, broker负责处理消息的"存储”(依赖bookkeeper实现)和分发, broker承担着类似代理的角色.
    • producer通过 partition router选择指定broker发送
    • consumer获取partition的元数据信息 ??????
  • qmq
    • 类似Rocketmq, 提供 topic、queue视图, 一个topic有多个queue组成,
    • 类似Rocketmq, 所有的topic/queue消息存储在一个commit log, 异构构建consumer queue.
    • producer ????
    • consumer ????

组件设计

  • Kafka:
    • zookeeper: 存储元数据
    • kafka controller: 监听zk的变化, 处理 分区/副本状态, 每个kafka集群使用一个kafka controller.
    • group coordinator: 管理 consumer group状态, 比如offset管理和consumer rebalance, 每个consumer group有一个group coordinator.
    • broker: 提供文件存储
  • Rocketmq:
    • namesrv负责元数据的存储, 只有简单的文件/内存实现, 不依赖zk
    • filter: 提供消息过滤服务
    • broker: 提供文件存储
  • pulsar:
    • zookeeper: 存储元数据
    • bookkeeper: 负责存储消息、offset
    • broker: broker和bookkeeper交互, 实现 消息+offset的存储
    • 存储的实现上, 依赖bookkeeper, bookkeeper能够高效的实现追加写, broker需要维护bookkeeper的topic和bookkeeper bookie的ledger信息, 映射维护在zk中.
    • function: 轻量的计算引擎
  • qmq:
    • metaserver提供元数据存储, 依赖mysql
    • delay-server: 基于时间轮提供了定时任务

扩容

在扩容方式中, 有两种, 希望增加consumer的消费能力需要扩容 和 降低单个broker的负载需要扩容.

consumer扩容

业务执行过程中, 单个consumer的执行能力是有上限的, 除了优化单个consumer的下游耗时, 比如rpc调用、本地计算等, 还有细化consumer的消费粒度, 比如partition级别的consumer, 通过并行partition的消费, 提升consumer的消费能力. 但是, partition consumer的消费能力也是有上限的, 这里分开讨论下.

  • 在kafka中, consumer最细的粒度是 partition级别的, 如果需要增加consumer的消费能力, 最终只能够扩容partition, 但是过多的partition, 在consumer发布中, 会导致频繁的rebalance.
  • 在Rocketmq, consumer最细的粒度queue, 如果需要增加consumer的消费能力, 只需要 producer用新的queueId向新的broker列表发送消息就可以. consumer端只需要连上新的broker即可, 发布过程无影响.
  • 在pulsar中, consumer最细的粒度是宿便 topic, 如果需要增加consumer的消费能力, 只需要添加更多的sub topic, 没有类似kafka的副作用
  • qmq中, 模式偏向Rocketmq, 也是异步构建consumer queue, 所以, 增加consumer的消费能力, 也需要添加更多的queue. 没有类似kafkade 副作用

broker扩容

在线上运行时, 会有很多topic/partition, 导致读写文件的OS Cache的红利降低, 这个时候, 需要扩容 broker, 降低单个broker的负载.

  • Kafka: 纯粹的添加broker到Kafka集群并不会引起broker的负载降低, 还需要配合脚本 kafka-reassign-partitions.sh

  • Rocketmq: 官方没有提供很好的方式, 可以参照文章, 需要自己改造

  • pulsar: 因为存储和分发分离的缘故, 所以只需要修改broker和topic的映射关系, 参见文章

  • qmq: qmq 使用range映射逻辑queue到物理queue的映射, 扩容中, 添加broker后, 还需要修改逻辑queue的映射, 之后qmq会完成整个流程. qmq扩容的实现中, 通过版本号区分不同时候的topic/queue -> broker的映射, 并且, 为了保证消息的顺序新, 提出了控制消息的想法, consumer在消费到控制消息之后, 取出里面的版本号, 拉取相应版本的映射关系, 再进行消费.