消息队列介绍与对比

本文作者分享了自己在不同项目中使用消息队列的经验,包括RabbitMQ、Kafka、NSQ和RocketMQ等,重点讨论了它们的特性、应用场景和选型原则,如消息顺序、重复/幂等、丢失和事务等。
摘要由CSDN通过智能技术生成

        消息队列不是什么新鲜玩意了,网上也是一大堆消息队列的介绍。本文只记录自己消息队列的使用过程,和自己总结的消息队列的对比。

        消息队列广泛应用主要得益于如下特性:

        1、非实时性。一些业务并不需要实时处理;

        2、异步。不需要同步进行处理不同业务,可异步去处理;

        3、解耦。将不同业务进行分离,生产者和消费者相互独立。

        4、流量削峰,限流缓解高并发。比如在秒杀中,经常会用到消息队列进行排队,缓解高并发压力。

        市面上开源的消息有很多,我最早接触的是RabbitMQ,因为当时自己网站用了celery,其内部就是使用了RabbitMQ,后来在2018年要换工作,去小米之前接触了golang的nsq,进入小米之后又开始使用了Kafka,过了两年,业务系统全部换成RocketMQ。就这样,我几乎把比较著名的消息队列都使用了一遍。总结一下我使用的场景:

       RabbitMQ:Celery,用于任务发布和消费执行,在我个人网站有充分应用;

       Kafka:早期小米有品订单支付后发送通知给小米OC,后期只使用Kafka做日志传输;

      NSQ:这个我纯粹是从学习的角度熟悉的,没有在实际工作中使用;

      RocketMQ:这个应该是我在实际工作中使用最多的,当年在小米几乎所有业务系统都使用RocketMQ作为唯一的业务消息中间件。

       Notify:这个是小米自研的,我们最早用过一段时间,说实话,我不是贬低他,但性能差得远了,最早版本是使用Mysql实现的,后来还是要借助RocketMQ,也就是说本身没什么意义了。 

     当我们在写业务中,如何做技术选型还是要因地制宜,从实际业务场景去出发,而不是一概而论。因为每个消息队列都有其特性,都会侧重其中某些方面。就像在小米时后来都选用RocketMQ时因为其更适用于我们电商的场景,其支持消息延迟、定时、半事务消息等等。而并不是说其他的不好。我现单位的EDA也是基于RocketMQ开发的。

        那做消息队列选型,就要从以下几个维度去考虑:

  • 消息顺序
  • 消息重复/消息幂等
  • 消息丢失
  • 消息事务
  • 消息延迟
  • 消费优先级
  • 消费持久化
  • 消息回溯  

          举个例子,如果对消息的丢失很看重,就一定注意持久化的问题。我们知道RabbitMQ的队列默认是auto-delete的,如果服务器重启了,队列都没了。我现单位就有一个项目组使用了RabbitMQ就出现了这个问题,服务器down掉后,重启后导致之前需要发送的消息全部丢失,这就是对消息队列不熟悉导致的。        

        

  • 消息顺序

        消息顺序通常指的是消费者在消费消息时要完全按照发送消息的顺序消费。先发送的要保证先消费。这种场景在电商中比较常见。比如一个订单,可能要经过已支付,已出库,已妥投等状态流转。某些消费者要求必须严格按照正向的订单流转消息去消费,已出库不能在已支付的消息之前消费。我们可以看下Rocketmq顺序消息的实现。

        生产顺序: 通过hashKey,将同一个key的消息一定发送到同一个队列上。每个队列的消息都是追加的,一定是按序存储的。不过在生产时,必须采用同步方式发送,否则异步的话不能保证一定是按序发送。

        消费顺序:Rocketmq保证在集群模式下,一个消息只能被同一个Consumer Group下的一个Consumer消费;广播模式下,就算是同一个group,也有可能被多个Consumer消费。

        所以如果想实现顺序消费,一个基本前提就是一定是集群模式。

  • 消息重复/消息幂等

        消息重复指的是消息是否回重复发送或者重复消费。也就是说,在生产者和消费者两端都可能产生消息重复。目前的消息队列很少有保证不重复的,即At Most once,通常是需要我们消费者去保证幂等性。即使相同的消息多次发送,消费者都应该有能力保证相同的消息只会处理一次。

  • 消息丢失

        消息丢失是指消息投递后,消费者还没来得及消费,消息就因为某种原因丢失了。目前市面的消息队列都能支持At least Once,可保证消息能够正常生产发送方到消息队列中,且在消费者消费之前不会丢失。

        当然,诸如RocketMQ所谓的保证At least Once,也并不是百分百的,因为数据首先回先写入到PageCache中的,如果恰好整个集群都在落盘之前宕机,就会出现丢失。当然这个发生的概率非常小。对,非常小,但不是没可能。

  • 消息事务

        我们通常所说的消息事务包括两种,一是实现分布式事务消息(本地事务执行成功,消息成功投递);二是多个消息同时发送,要么都成功,要么都失败。RocketMQ实现的是前者,通过消息分布式事务能力,可以确保生产者只有成功执行本地事务,才能够将消息发送到消息队列,并对消费者可见。目前提供这种能力的消息队列比较少,而RocketMQ提供了这样的能力,非常适用于分布式系统。这个地方可以看下RocketMQ官网对于半事务的介绍,我贴一下官网的示意图:

        Kafka实现的是后者,即保证多个消息同时发送时,要么都成功,要么都失败,其内部通过事务协调器来实现。下面是一张示意图:

  • 消息延迟

        延迟强调的是延迟消费,Producer生产消息发送到消息队列后,不是立即对消费者可见,而是延迟一段时间之后,消费者才可消费。在电商中这种场景也是很常见的。比如下单后半个小时或一个小时之后还未支付的话就执行关单操作。目前很多消息队列都支持,比如RocketMQ,DDMQ,QMQ等,Kafka的并不对外提供这种能力,但内部有自用的定时任务,是通过时间轮实现的。   消息延迟这块儿我会单独写一篇文章来说。

  • 消费优先级

        通过设置消息优先级,可以实现某些消息可以优先被消费。说到这个功能,只有在生产者生产消息的速度远远大于消费者消费消息的速度才有意义。RabbitMQ对消息优先级的支持比较好,他通过设置不同优先级的队列来实现。

  • 消费持久化

        持久化是一个很重要的功能,如果消息队列的server宕机了,可以方便恢复当前所有生产的消息以及消费者消费消息进度等信息。目前几乎所有的消息队列都是支持持久化的,算得上是基本能力。不过持久化的存在势必会在一定程度上影响系统性能,因为写入磁盘是一件相对比较耗时的操作,不过这是一个可以优化的过程,比如通过异步、顺序读写来提升。

    消息队列对比详情

类型

RocketMQ

Kafka

Rabbitmq

消息重复/幂等

不保证消息重复,需要在消费端自行实现。

早期版本不保证消息重复,需要在消费端自行实现。自从0.11.0.0之后,可实现生产消息不重复,Exactly Once。小米之前还发了一个基于Kafka的Talos,实际上也是通过生成唯一id保证的。

不保证消息重复,需要在消费端自行实现

消息持久化

支持,持久化到文件

RocketMQ的文件是存在commitLog,顺序写,通过mmap零拷贝技术提升读写性能,当然也有一个Index File索引文件,这个和Kafka差不多的

支持,持久化到文件。和RocketMQ的定长文件不同,Kafka的文件分区存储。

需要配置队列和消息的持久化。

消息丢失

可以保证at least once ,因此不会丢失。

在生产端,只有broker回复SUCCESS,生产端才为投递成功;

在消费端,只有回传SUCCESS给broker,才被认为消费成功。

当然,发送模式应该是同步活异步,如果发送模式选择oneway则不保证。

但有一种情况可能出现丢失:broker集群收到消息了,但是采用异步刷盘的话,可能在落盘之前,整个集群都挂掉了,当然发生概率会很低。

同理,也可保证消息不丢失。其实RocketMQ也是参考kafka来实现的。同样在生产消息以及消费消息的过程都会有确认机制。

可以保证不丢失

但是 队列和消息都要配置持久化,否则只在内存中,还是有可能丢失的。

消息顺序

在同一个逻辑队列中,可以实现消息的顺序。全局也可以,但是效率较差。

当使用顺序消息时,消费端同样要改成顺序消费,其默认是并发消费,即会同时开多个线程并发消费。

同一个partition下的可以实现消息顺序。

同理,将要顺序消费的消息放在一个Queue

消息延迟

支持延迟,延迟实现的方式(开源版)是设置不同级别的延迟队列,队列中通过定时去处理到期消息,其共有18个级别的定时。

在到时之前投递在一个专门的TOPIC下。

这个地方,小米后来是借助滴滴方式实现了任意时间的延迟。

这里要说一下,商业版是支持任意时间,嗯,阿里很会玩儿。

不支持。但内部通过时间轮实现了定时任务,不对外提供相应功能。

本身不支持,但可通过延迟插件或者死信队列方式实现。可以访问下面的参考资料

消息回溯

由于最后文件是写到磁盘文件,是支持消息回溯的。但过期删除的查看不了,一般上可配置7天或一个月。

支持回溯,支持按照offset和timestamp两种维度进行回溯。

不支持,当消息被消费之后,就会被删除。

消息事务

支持在生产者和broker之间的半事务。具体流程可参考官网。简单说就是通过双方的commit,以及事务状态回查来实现。

在事务完全提交之前,是个半事务消息,也不会消费。

自0.11开始,kafka就支持了事务。但这个事务和RocketMQ的事务不是一回事,RocketMQ强调分布式事务的场景。Kafka强调的是一次发多个消息的事务特性。即多个消息要么都成功,要么都失败。

虽然Rabbitmq事务机制,但感觉其并非为了支持分布式事务,而只是一种支持At least once的保证机制。

消费重试

集群模式下,如果未成功消费,可以重试,超过一定次数进入死信队列;

广播模式下,不支持重试。

不支持。和RocketMQ不同,kafka不需要消费者提供消费ack,而且不自动维护偏移量,只是提供偏移量更新接口。不过可以根据这个特性实现自动重试。

支持。默认支持消费端的确认机制。

消费分组

支持消费分组

支持消费分组

不支持,一个消息只能被一个消费者消费

高可用

设置NameServer集群,每个节点都会存储全部的路由和topic信息。

broker集群:多master+多slave

且 broker有专门负责master到slave的同步

集群部署,早期使用ZK管理。后期自实现KRaft进行集群管理。

有主队列和镜像队列,可以保证高可用。但要特别注意集群内的节点同步只同步队列元数据,不同步队列数据本身,RabbitMQ主要是基于性能考虑。

高性能

通过顺序IO、零拷贝mmap)、PageCache、分区、索引文件等多种方式来实现高可用。

支持消费端多线程并发消费。

通过顺序IO、零拷贝(sendfile)、PageCache、分区、索引文件等多种方式来实现高性能

性能一般,主要其采用Push模式

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值