RabbitMQ,Kafaka、RocketMQ、Pulsar消息队列的区别

一、简介

ActiveMQ

        ActiveMQ 由 Apache 软件基金会基于 Java 语言开发的一个开源的消息代理,实现了JMS。

RabbitMQ

        RabbitMQ 是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ 服务器是用 Erlang 语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。RabbitMQ 支持多种消息传递协议、传递确认等特性。

Kafka

        Apache Kafka 是由 Apache 软件基金会开发的一个开源消息系统项目,由 Scala 写成。

        Kafka 最初是由 LinkedIn 开发,并于 2011 年初开源。2012 年 10 月从 Apache Incubator 毕业。

        该项目的目标是为处理实时数据提供一个统一、高通量、低等待的平台。Kafka 是一个分布式的、分区的、多复本的日志提交服务。它通过一种独一无二的设计提供了一个消息系统的功能。

RocketMQ

        Apache RocketMQ 是一个分布式消息和流媒体平台,具有低延迟、强一致、高性能和可靠性、万亿级容量和灵活的可扩展性。它有借鉴 Kafka 的设计思想,但不是 Kafka 的拷贝。 基于JMS实现。

Pulsar

        Apache Pulsar 是 Apache 软件基金会顶级项目,是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体,采用计算与存储分离架构设计。支持多租户、持久化存储、多机房跨区域数据复制,具有强一致性、高吞吐、低延时及高可扩展性等流数据存储特性,被看作是云原生时代实时消息流传输、存储和计算最佳解决方案。

更多详细介绍请翻阅文档:《RabbitMQ,RocketMQ,Kafka,Pulsar 几种消息队列的对比

二、功能及性能对比

  • 消费推拉模式

客户端消费者获取消息的方式,Kafka和RocketMQ是通过长轮询Pull的方式拉取消息,RabbitMQ、Pulsar、NSQ都是通过Push的方式。

pull类型的消息队列更适合高吞吐量的场景,允许消费者自己进行流量控制,根据消费者实际的消费能力去获取消息。而push类型的消息队列,实时性更好,但需要有一套良好的流控策略(backpressure)当消费者消费能力不足时,减少push的消费数量,避免压垮消费端。

  • 延迟队列

消息延迟投递,当消息产生送达消息队列时,有些业务场景并不希望消费者立刻收到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。延迟队列一般分为两种,基于消息的延迟和基于队列的延迟。基于消息的延迟指为每条消息设置不同的延迟时间,当队列有新消息进入的时候根据延迟时间排序,当然这样会对性能造成较大影响。另一种基于队列的延迟指的是设置不同延迟级别的队列,队列中每个消息的延迟时间都是相同的,这样免去了基于延迟时间排序对性能带来的损耗,通过一定的扫描策略即可投递超时的消息。

延迟消息的使用场景比如异常检测重试,订单超时取消等,例如:

  • 服务请求异常,需要将异常请求放到单独的队列,隔5分钟后进行重试;
  • 用户购买商品,但一直处于未支付状态,需要定期提醒用户支付,超时则关闭订单;
  • 面试或者会议预约,在面试或者会议开始前半小时,发送通知再次提醒。

Kafka不支持延迟消息。Pulsar支持秒级的延迟消息,所有延迟投递的消息会被Delayed Message Tracker记录对应的index,consumer在消费时,会先去Delayed Message Tracker检查,是否有到期需要投递的消息,如果有到期的消息,则从Tracker中拿出对应的index,找到对应的消息进行消费,如果没有到期的消息,则直接消费正常的消息。对于长时间的延迟消息,会被存储在磁盘中,当快到延迟间隔时才被加载到内存里。

RocketMQ开源版本延迟消息临时存储在一个内部主题中,不支持任意时间精度,支持特定的level,例如定时5s,10s,1m等。

RabbitMQ需要安装一个rabbitmq_delayed_message_exchange插件。

NSQ通过内存中的优先级队列来保存延迟消息,支持秒级精度,最多2个小时延迟。

  • 死信队列

由于某些原因消息无法被正确的投递,为了确保消息不会被无故的丢弃,一般将其置于一个特殊角色的队列,这个队列一般称之为死信队列。与此对应的还有一个“回退队列”的概念,试想如果消费者在消费时发生了异常,那么就不会对这一次消费进行确认(Ack), 进而发生回滚消息的操作之后消息始终会放在队列的顶部,然后不断被处理和回滚,导致队列陷入死循环。为了解决这个问题,可以为每个队列设置一个回退队列,它和死信队列都是为异常的处理提供的一种机制保障。实际情况下,回退队列的角色可以由死信队列和重试队列来扮演。

Kafka没有死信队列,通过Offset的方式记录当前消费的偏移量。

Pulsar有重试机制,当某些消息第一次被消费者消费后,没有得到正常的回应,则会进入重试Topic中,当重试达到一定次数后,停止重试,投递到死信Topic中。

RocketMQ通过DLQ来记录所有消费失败的消息。

RabbitMQ是利用类似于延迟队列的形式实现死信队列。

NSQ没有死信队列。

  • 优先级队列

优先级队列不同于先进先出队列,优先级高的消息具备优先被消费的特权,这样可以为下游提供不同消息级别的保证。不过这个优先级也是需要有一个前提的:如果消费者的消费速度大于生产者的速度,并且消息中间件服务器(一般简单的称之为Broker)中没有消息堆积,那么对于发送的消息设置优先级也就没有什么实质性的意义了,因为生产者刚发送完一条消息就被消费者消费了,那么就相当于Broker中至多只有一条消息,对于单条消息来说优先级是没有什么意义的。

Kafka、RocketMQ、Pulsar、NSQ不支持优先级队列,可以通过不同的队列来实现消息优先级。

RabbitMQ支持优先级消息。

  • 消息回溯

一般消息在消费完成之后就被处理了,之后再也不能消费到该条消息。消息回溯正好相反,是指消息在消费完成之后,还能消费到之前被消费掉的消息。对于消息而言,经常面临的问题是“消息丢失”,至于是真正由于消息中间件的缺陷丢失还是由于使用方的误用而丢失一般很难追查,如果消息中间件本身具备消息回溯功能的话,可以通过回溯消费复现“丢失的”消息进而查出问题的源头之所在。消息回溯的作用远不止与此,比如还有索引恢复、本地缓存重建,有些业务补偿方案也可以采用回溯的方式来实现。

Kafka支持消息回溯,可以根据时间戳或指定Offset,重置Consumer的Offset使其可以重复消费。

Pulsar支持按时间对消息进行回溯。

RocketMQ支持按时间回溯,实现的原理跟Kafka一致。

RabbitMQ不支持回溯,消息一旦标记确认就会被标记删除。

NSQ一般消息是不可回溯的,但可以通过nsq_to_file工具,将消息写入到文件,然后从文件里重放消息。

  • 消息持久化

流量削峰是消息中间件的一个非常重要的功能,而这个功能其实得益于其消息堆积能力。从某种意义上来讲,如果一个消息中间件不具备消息堆积的能力,那么就不能把它看做是一个合格的消息中间件。消息堆积分内存式堆积和磁盘式堆积。一般来说,磁盘的容量会比内存的容量要大得多,对于磁盘式的堆积其堆积能力就是整个磁盘的大小。从另外一个角度讲,消息堆积也为消息中间件提供了冗余存储的功能。

Kafka和RocketMQ直接将消息刷入磁盘文件中进行持久化,所有的消息都存储在磁盘中。只要磁盘容量够,可以做到无限消息堆积。

RabbitMQ 是典型的内存式堆积,但这并非绝对,在某些条件触发后会有换页动作来将内存中的消息换页到磁盘(换页动作会影响吞吐),或者直接使用惰性队列来将消息直接持久化至磁盘中。

Pulsar消息是存储在BookKeeper存储集群上,也是磁盘文件。

NSQ通过nsq_to_file工具,将消息写入到文件。

  • 消息确认机制

消息队列需要管理消费进度,确认消费者是否成功处理消息,使用push的方式的消息队列组件往往是对单条消息进行确认,对于未确认的消息,进行延迟重新投递或者进入死信队列。

Kafka通过Offset的方式确认消息。

RocketMQ与Kafka类似也会提交Offset,区别在于消费者对于消费失败的消息,可以标记为消息消费失败,Broker会重试投递,如果累计多次消费失败,会投递到死信队列。

RabbitMQ和NSQ类似,消费者确认单条消息,否则会重新放回队列中等待下次投递。

Pulsar使用专门的Cursor管理。累积确认和Kafka效果一样;提供单条或选择性确认。

  • 消息TTL

消息TTL表示一条消息的生存时间,如果消息发出来后,在TTL的时间内没有消费者进行消费,消息队列会将消息删除或者放入死信队列中。

Kafka根据设置的保留期来删除消息。有可能消息没被消费,过期后被删除。不支持TTL。

Pulsar支持TTL,如果消息未在配置的TTL时间段内被任何消费者使用,则消息将自动标记为已确认。消息保留期与消息TTL之间的区别在于:消息保留期作用于标记为已确认并设置为已删除的消息,而TTL作用于未ack的消息。上面的图例中说明了Pulsar中的TTL。例如,如果订阅B没有活动消费者,则在配置的TTL时间段过后,消息M10将自动标记为已确认,即使没有消费者实际读取该消息。

RocketMQ提及到消息TTL的资料比较少,不过看接口似乎是支持的。

RabbitMQ有两种方式,一个是声明队列的时候在队列属性中设置,整个队列中的消息都有相同的有效期。还可以发送消息的时候给消息设置属性,可以位每条消息都设置不同的TTL。

NSQ似乎还没支持,有一个Feature Request的Issue处于Open状态。

  • 多租户隔离

多租户是指通过一个软件实例为多个租户提供服务的能力。租户是指对系统有着相同“视图”的一组用户。不支持多租户的系统里边,往往要为不同用户或者不同集群创建多个消息队列实例实现物理隔离,这样会带来较高的运维成本。作为一种企业级的消息系统,Pulsar的多租户能力按照设计可满足下列需求:

  • 确保严苛的SLA可顺利满足。
  • 保证不同租户之间的隔离。
  • 针对资源利用率强制实施配额。
  • 提供每租户和系统级的安全性。
  • 确保低成本运维以及尽可能简单的管理。

Pulsar通过下列方式满足了上述需求:

  • 通过为每个租户进行身份验证、授权和ACL(访问控制列表)获得所需安全性。
  • 为每个租户强制实施存储配额。

以策略的方式定义所有隔离机制,策略可在运行过程中更改,借此降低运维成本并简化管理工作。

  • 消息顺序性

消息顺序性是指保证消息有序。消息消费顺序跟生产的顺序保持一致。

Kafka保证了分区内的消息有序。

Pulsar支持两种消费模式,独占订阅的流模式只保证了消息的顺序性,共享订阅队列模型不保证有序性。

RocketMQ需要用到锁来保证一个队列同时只有一个消费者线程进行消费,保证消息的有序性。

RabbitMQ顺序性的条件比较苛刻,需要单线程发送、单线程消费,并且不采用延迟队列、优先级队列等高级功能。

NSQ是利用了golang自身的case/select实现的消息分发,本身不提供有序性保障,不能够把特性消息和消费者对应起来,无法实现消息的有序性。

  • 消息查询

在实际开发中,经常要查看MQ中消息的内容,比如通过某个MessageKey/ID,查询到MQ的具体消息。或者是对消息进行链路追踪,知道消息从哪里来,发送到哪里去,进而快速对问题进行排查定位。

Kafka存储层是以分布式提交日志的形式实现,每次写操作都顺序追加到日志的末尾。读也是顺序读。不支持检索功能。

Pulsar可以通过消息ID,查询到具体某条消息的消息内容、消息参数和消息轨迹。

RocketMQ支持按Message Key、Unique Key、Message Id对消息进行查询。

RabbitMQ使用基于索引的存储系统。这些将数据保存在树结构中,以提供确认单个消息所需的快速访问。由于RabbitMQ的消息在确认后会被删除,因此只能查询未确认的消息

NSQ自身不支持消息持久化和消息检索,不过可以使用nsq_to_http等工具将消息写入可支持索引的存储里。

  • 消费模式

Kafka有两种消费模式,最终都会保证一个分区只有1个消费者在消费:

  • subscribe方式:当主题分区数量变化或者consumer数量变化时,会进行rebalance;注册rebalance监听器,可以手动管理offset不注册监听器,kafka自动管理。
  • assign方式:手动将consumer与partition进行对应,kafka不会进行rebanlance。

Pulsar有以下四种消费模式,其中独占模式和灾备模式跟Kafka类似,为流模型,每个分区只有1个消费者消费,能保证消息有序性。共享模式和Key共享模式为队列模型,多个消费者能提高消费速度,但不能保证有序性。

  • Exclusive独占模式(默认模式):一个Subscription只能与一个Consumer关联,只有这个Consumer可以接收到Topic的全部消息,如果该Consumer出现故障了就会停止消费。

  • 灾备模式(Failover):当存在多个consumer时,将会按字典顺序排序,第一个consumer被初始化为唯一接受消息的消费者。当第一个consumer断开时,所有的消息(未被确认和后续进入的)将会被分发给队列中的下一个consumer。

  • 共享模式(Shared):消息通过round robin轮询机制(也可以自定义)分发给不同的消费者,并且每个消息仅会被分发给一个消费者。当消费者断开连接,所有被发送给他,但没有被确认的消息将被重新安排,分发给其它存活的消费者。

  • KEY共享模式(Key_Shared):当存在多个consumer时,将根据消息的 key进行分发,key相同的消息只会被分发到同一个消费者。

RocketMQ有两种消费模式,BROADCASTING广播模式,CLUSTERING集群模式。

广播消费指的是:一条消息被多个consumer消费,即使这些consumer属于同一个ConsumerGroup,消息也会被ConsumerGroup中的每个Consumer都消费一次,广播消费中ConsumerGroup概念可以认为在消息划分方面无意义。

集群消费模式:一个ConsumerGroup中的Consumer实例平均分摊消费消息。例如某个Topic有9条消息,其中一个ConsumerGroup有3个实例(可能是3个进程,或者3台机器),那么每个实例只消费其中部分,消费完的消息不能被其他实例消费。

RabbitMQ和NSQ的消费比较类似,都是跟Pulsar共享模式类似的,队列的形式,增加一个消费者组里的消费者数量能提高消费速度。

  • 消息可靠性

消息丢失是使用消息中间件时所不得不面对的一个同点,其背后消息可靠性也是衡量消息中间件好坏的一个关键因素。尤其是在金融支付领域,消息可靠性尤为重要。比如当服务出现故障时,一些对于生产者来说已经生产成功的消息,是否会在高可用切换时丢失。同步刷盘是增强一个组件可靠性的有效方式,消息中间件也不例外,Kafka和RabbitMQ都可以支持同步刷盘,但绝大多数情景下,一个组件的可靠性不应该由同步刷盘这种极其损耗性能的操作来保障,而是采用多副本的机制来保证。

Kafka可以通过配置request.required.acks参数设置可靠级别,表示一条消息有多少个副本确认接收成功后,才被任务发送成功。

  • request.required.acks=-1 (全量同步确认,强可靠性保证)
  • request.required.acks=1(leader确认收到,默认)
  • request.required.acks=0 (不确认,但是吞吐量大)

Pulsar有跟Kafka类似的概念,叫Ack Quorum Size(Qa),Qa是每次写请求发送完毕后需要回复确认的Bookie的个数,其数值越大则需要确认写成功的时间越长,其值上限是副本数Qw。为了一致性,Qa应该是:(Qw+1)/2或者更,即为了确保数据安全性,Qa下限是 (Qw+1)/2。

RocketMQ与Kafka类似。

RabbitMQ是主从架构,通过镜像环形队列实现多副本及强一致性语义的。多副本可以保证在master节点宕机异常之后可以提升slave作为新的master而继续提供服务来保障可用性。

NSQ会通过go-diskqueue组件将消息落盘到本地文件中,通过mem-queue-size参数控制内存中队列大小,如果mem-queue-size=0每条消息都会存储到磁盘里,不用担心节点重启引起的消息丢失。但由于是存储在本地磁盘中,如果节点离线,堆积在节点磁盘里的消息会丢失。

猜你喜欢

转载自blog.csdn.net/qq_42014561/article/details/128614864