mq 概念介绍

介绍

 

RabbitMQ

  组件

  • Broker :一个RabbitMQ实例就是一个Broker
  • Virtual Host :虚拟主机。相当于MySQL的DataBase,一个Broker上可以存在多个vhost,vhost之间相互隔离。每个vhost都拥有自己的队列、交换机、绑定和权限机制。vhost必须在连接时指定,默认的vhost是/。
  • Exchange :交换机,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
  • Queue :消息队列,用来保存消息直到发送给消费者。它是消息的容器。一个消息可投入一个或多个队列。
  • Banding :绑定关系,用于消息队列和交换机之间的关联。通过路由键(Routing Key)将交换机和消息队列关联起来。
  • Channel :管道,一条双向数据流通道。不管是发布消息、订阅队列还是接收消息,这些动作都是通过管道完成。因为对于操作系统来说,建立和销毁TCP都是非常昂贵的开销,所以引入了管道的概念,以复用一条TCP连接。
  • Connection :生产者/消费者 与broker之间的TCP连接。
  • Publisher :消息的生产者。
  • Consumer :消息的消费者。
  • Message :消息,它是由消息头和消息体组成。消息头则包括Routing-Key、Priority(优先级)等。
 

交换机类型

Exchange 分发消息给 Queue 时,Exchange 的类型对应不同的分发策略,以下三种:
    • Direct:消息中的 Routing Key 如果和 Binding 中的 Routing Key 完全一致, Exchange 就会将消息分发到对应的队列中。
    • Fanout:每个发到 Fanout 类型交换机的消息都会分发到所有绑定的队列上去。Fanout交换机没有 Routing Key 。它在三种类型的交换机中转发消息是最快的。
    • Topic:Topic交换机通过模式匹配分配消息,将 Routing Key 和某个模式进行匹配。它只能识别两个通配符:"#"和"*"。### 匹配0个或多个单词, * 匹配1个单词。
 

TTL

RabbitMQ支持消息的过期时间
    • 在消息发送时进行指定。通过配置消息体的 Properties ,可以指定当前消息的过期时间。
    • 在创建Exchange时指定。从进入消息队列开始计算,只要超过了队列的超时时间配置,那么消息会自动清除。
 

生产者的消息确认机制

Confirm机制:

    • 消息的确认,是指生产者投递消息后,如果Broker收到消息,则会给我们生产者一个应答。
    • 生产者进行接受应答,用来确认这条消息是否正常的发送到了Broker,这种方式也是消息的可靠性投递的核心保障!
如何实现Confirm确认消息:
    • 在channel上开启确认模式
    • 在channel上开启监听:addConfirmListener ,监听成功和失败的处理结果,根据具体的结果对消息进行重新发送或记录日志处理等后续操作。

Return消息机制:

正常情况下消息生产者,通过指定一个Exchange和Routing,把消息送达到某一个队列中去,然后我们的消费者监听队列进行消息的消费处理操作。如果我们在发送消息的时候,当前的exchange不存在或者指定的路由key路由不到,这个时候我们需要监听这种不可达消息,就需要使用到Returrn Listener。
    • 基础API中有个关键的配置项 Mandatory :如果为true,监听器会收到路由不可达的消息,然后进行处理。如果为false,broker端会自动删除该消息。
    • 通过监听的方式, chennel.addReturnListener(ReturnListener rl) 传入已经重写过handleReturn方法的ReturnListener。
 

消费端ACK与NACK

需要手动ACK保障消费端消费成功,消息在消费端重回队列为了对没有成功处理消息,把消息重新返回到Broker,一般来说,实际应用中都会关闭重回队列(避免进入死循环),也就是设置为false。
 
 

死信队列DLX

死信队列(DLX Dead-Letter-Exchange):当消息在一个队列中变成死信之后,它会被重新推送到另一个队列,这个队列就是死信队列。DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。
当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列。
 
--------------------------------------------------------------------------------------------
 

RocketMQ

核心概念

    • Broker:消息中转角色,负责存储消息,转发消息。Broker是具体提供业务的服务器,单个Broker节点与所有的NameServer节点保持长连接及心跳,并会定时将Topic信息注册到NameServer,顺带一提底层的通信和连接都是基于Netty实现的。Broker负责消息存储,以Topic为纬度支持轻量级的队列,单机可以支撑上万队列规模,支持消息推拉模型。官网上有数据显示:具有上亿级消息堆积能力,同时可严格保证消息的有序性。
    • Topic:主题!它是消息的第一级类型。比如一个电商系统可以分为:交易消息、物流消息等,一条消息必须有一个 Topic 。Topic与生产者和消费者的关系非常松散,一个 Topic 可以有0个、1个、多个生产者向其发送消息,一个生产者也可以同时向不同的 Topic 发送消息。一个 Topic 也可以被 0个、1个、多个消费者订阅。
    • Tag:标签!可以看作子主题,它是消息的第二级类型,用于为用户提供额外的灵活性。使用标签,同一业务模块不同目的的消息就可以用相同Topic而不同的Tag来标识。比如交易消息又可以分为:交易创建消息、交易完成消息等,一条消息可以没有Tag。标签有助于保持您的代码干净和连贯,并且还可以为RabbitMQ提供的查询系统提供帮助。
    • MessageQueue:一个Topic下可以设置多个消息队列,发送消息时执行该消息的Topic,RocketMQ会轮询该Topic下的所有队列将消息发出去。消息的物理管理单位。一个Topic下可以有多个Queue,Queue的引入使得消息的存储可以分布式集群化,具有了水平扩展能力。
    • NameServer:类似Kafka中的ZooKeeper,但NameServer集群之间是没有通信的,相对ZK来说更加轻量。它主要负责对于源数据的管理,包括了对于Topic和路由信息的管理。每个Broker在启动的时候会到NameServer注册,Producer在发送消息前会根据Topic去NameServer获取对应Broker的路由信息,Consumer也会定时获取 Topic 的路由信息。
    • Producer:生产者,支持三种方式发送消息:同步、异步和单向单向发送 :消息发出去后,可以继续发送下一条消息或执行业务代码,不等待服务器回应,且没有回调函数。异步发送 :消息发出去后,可以继续发送下一条消息或执行业务代码,不等待服务器回应,有回调函数。同步发送 :消息发出去后,等待服务器响应成功或失败,才能继续后面的操作。
    • Consumer:消费者,支持 PUSH 和 PULL 两种消费模式,支持集群消费和广播消费集群消费 :该模式下一个消费者集群共同消费一个主题的多个队列,一个队列只会被一个消费者消费,如果某个消费者挂掉,分组内其它消费者会接替挂掉的消费者继续消费。广播消费 :会发给消费者组中的每一个消费者进行消费。相当于RabbitMQ的发布订阅模式。
    • Group:分组,一个组可以订阅多个Topic。分为ProducerGroup,ConsumerGroup,代表某一类的生产者和消费者,一般来说同一个服务可以作为Group,同一个Group一般来说发送和消费的消息都是一样的
    • Offset:在RocketMQ中,所有消息队列都是持久化,长度无限的数据结构,所谓长度无限是指队列中的每个存储单元都是定长,访问其中的存储单元使用Offset来访问,Offset为Java Long类型,64位,理论上在 100年内不会溢出,所以认为是长度无限。也可以认为Message Queue是一个长度无限的数组,Offset就是下标。
 

延时消息

开源版的RocketMQ不支持任意时间精度,仅支持特定的level,例如定时5s,10s,1min等。其中,level=0级表示不延时,level=1表示1级延时,level=2表示2级延时,以此类推。
延时等级如下:
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
 

顺序消息

消息有序指的是可以按照消息的发送顺序来消费(FIFO)。RocketMQ可以严格的保证消息有序,可以分为 分区有序 或者 全局有序 。
 

事务消息

事务消息发送及提交:
    • 发送half消息
    • 服务端响应消息写入结果
    • 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行);
    • 根据本地事务状态执行Commit或Rollback(Commit操作生成消息索引,消息对消费者可见)。
事务消息的补偿流程:
    • 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”;
    • Producer收到回查消息,检查回查消息对应的本地事务的状态。
    • 根据本地事务状态,重新Commit或RollBack
事务消息状态:
提交状态、回滚状态、中间状态:
TransactionStatus.CommitTransaction:提交事务,它允许消费者消费此消息。
TransactionStatus.RollbackTransaction:回滚事务,它代表该消息将被删除,不允许被消费。
TransactionStatus.Unkonwn:中间状态,它代表需要检查消息队列来确定消息状态。
 

死信队列

当一条消息消费失败,RocketMQ就会自动进行消息重试。而如果消息超过最大重试次数,RocketMQ就会认为这个消息有问题。但是此时,RocketMQ不会立刻将这个有问题的消息丢弃,而会将其发送到这个消费者组对应的一种特殊队列:死信队列。
特性:
    • 一个死信队列对应一个Group ID, 而不是对应单个消费者实例。
    • 如果一个Group ID未产生死信消息,消息队列RocketMQ不会为其创建相应的死信队列。
    • 一个死信队列包含了对应Group ID产生的所有死信消息,不论该消息属于哪个Topic。
 
--------------------------------------------------------------------------------------------
 

Kafka

Kafka是一个分布式、支持分区的、多副本的,基于ZooKeeper协调的分布式消息系统。它最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于Hadoop的批处理系统、低延迟的实时系统、Storm/Spark流式处理引擎,Web/Nginx日志、访问日志,消息服务等等,用Scala语言编写。
 

核心概念:

    • Broker:消息中间件处理节点,一个Kafka节点就是一个Broker,一个或者多个Broker可以组成一个Kafka集群
    • Topic:Kafka根据topic对消息进行归类,发布到Kafka集群的每条消息都需要指定一个topic
    • Producer:消息生产者,向Broker发送消息的客户端
    • Consumer:消息消费者,从Broker读取消息的客户端
    • ConsumerGroup:每个Consumer属于一个特定的ConsumerGroup,一条消息可以被多个不同的ConsumerGroup消费,但是一个ConsumerGroup中只能有一个Consumer能够消费该消息
    • Partition:物理上的概念,一个topic可以分为多个partition,每个partition内部消息是有序的
    • Leader:每个Partition有多个副本,其中有且仅有一个作为Leader,Leader是负责数据读写的Partition。
    • Follower:Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,Leader会把这个Follower从 ISR列表 中删除,重新创建一个Follower。
    • Offset:偏移量。Kafka的存储文件都是按照offset.kafka来命名,用Offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。

理解

一个Topic,代表逻辑上的一个业务数据集,比如订单相关操作消息放入订单Topic,用户相关操作消息放入用户Topic,对于大型网站来说,后端数据都是海量的,订单消息很可能是非常巨量的,比如有几百个G甚至达到TB级别,如果把这么多数据都放在一台机器上可定会有容量限制问题,那么就可以在Topic内部划分多个Partition来分片存储数据,不同的Partition可以位于不同的机器上,相当于分布式存储。每台机器上都运行一个Kafka的进程Broker。
 
--------------------------------------------------------------------------------------------
 

如何保证顺序消费?

  • RabbitMQ:一个Queue对应一个Consumer即可解决。
  • RocketMQhash(key)%队列数
  • Kafka:hash(key)%分区数
 

如何实现延迟消费?

  • RabbitMQ:两种方案
    • 死信队列 + TTL引入RabbitMQ的延迟插件
  • RocketMQ:天生支持延时消息。
  • Kafka:专门为要延迟的消息创建一个Topic新建一个消费者去消费这个Topic消息持久化再开一个线程定时去拉取持久化的消息,放入实际要消费的Topic实际消费的消费者从实际要消费的Topic拉取消息。
 

可靠性投递

  • RabbitMQ:
Broker-->消费者:手动ACK
生产者-->Broker:两种方案
数据库持久化:

 

1.将业务订单数据和生成的Message进行持久化操作(一般情况下插入数据库,这里如果分库的话可能涉及到分布式事务) 2.将Message发送到Broker服务器中 3.通过RabbitMQ的Confirm机制,在producer端,监听服务器是否ACK。 4.如果ACK了,就将Message这条数据状态更新为已发送。如果失败,修改为失败状态。 5.分布式定时任务查询数据库3分钟(这个具体时间应该根据的时效性来定)之前的发送失败的消息 6.重新发送消息,记录发送次数 7.如果发送次数过多仍然失败,那么就需要人工排查之类的操作。
 

消息的延迟投递:

 

1.将业务订单持久化 2.发送一条Message到broker(称之为主Message),再发送相同的一条到不同的队列或者交换机(这条称为确认Message)中。 3.主Message由实际业务处理端消费后,生成一条响应Message。之前的确认Message由Message Service应用处理入库。 4~6.实际业务处理端发送的确认Message由Message Service接收后,将原Message状态修改。 7.如果该条Message没有被确认,则通过rpc调用重新由producer进行全过程。
 
 
  • RocketMQ

生产者:3种方式
同步发送 异步发送 单向发送
Broker:
创建Queue的时候设置持久化,保证Broker持久化Queue的元数据,但是不会持久化Queue里面的消息 将Message的deliveryMode设置为2,可以将消息持久化到磁盘,这样只有Message支持化到磁盘之后才会发送通知Producer ack
消费者:
Consumer有消费到Message,但是内部出现问题,Message还没处理,Broker以为Consumer处理完了,只会把后续的消息发送。这时候,就要 关闭autoack,消息处理过后,进行手动ack , 多次消费失败的消息,会进入 死信队列 ,这时候需要人工干预。
  • Kafka

生产者:设置了 acks=all ,一定不会丢,要求是,你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。
Broker:脑裂现象
replication.factor min.insync.replicas acks=all retries=MAX
消费者:
消费者那边自动提交了 offset,但是未处理完成场景, 关闭自动提交 offset,只有完成消费了才提交offset,且要保证幂等性防止处理完成offset未提交就宕掉。
 
消息堆积
临时写一个消息分发的消费者,把积压队列里的消息均匀分发到N个队列中,同时一个队列对应一个消费者,相当于消费速度提高了N倍。
积压时间太久,导致部分消息过期,怎么处理?
批量重导。在业务不繁忙的时候,比如凌晨,提前准备好程序,把丢失的那批消息查出来,重新导入到MQ中。
 
 
--------------------------------------------------------------------------------------------

kafka和rabbitmq区别总结

 

消费顺序:

  • rabbitmq

    • 为了实现发布订阅功能,从而使用的消息复制,会降低性能并耗费更多资源
    • 多个消费者无法严格保证消息顺序,且消费者出错后会重新入队。
    • 大量的订单集中在一个队列,吞吐量受到了限制
  • kafka

    • 发布订阅并不会复制消息,因为 Kafka 的发布订阅就是消费者直接去获取被 Kafka 保存在日志文件中的消息就好。无论是多少消费者,他们只需要主动去找到消息在文件中的位置即可。
    • 不会出现消费者出错后,把消息重新入队的现象。
    • 可以对订单进行分区,把不同订单分到多个分区中保存,这样,吞吐量能更好。

消息的匹配:

  • rabbitMQ

允许在消息中添加 routing_key 或者自定义消息头,然后通过一些特殊的 Exchange,很简单的就实现了消息匹配分发。开发几乎不用成本。
  • kafka

消费者端必须先把所有消息不管需要不需要,都取出来。然后,再根据业务需求,自己去实现各种精准和模糊匹配。可能因为过度的复杂性,还要引入规则引擎。
 
 

消息的超时:

  • rabbitmq

延迟队列 RabbitMQ 最简单的实现方式就是设置 TTL,然后一个消费者去监听死信队列。当消息超时了,监听死信队列的消费者就收到消息了。
  • kafka

先需要把消息先放入一个临时的 topic。
开发一个做中转的消费者。让这个消费者先去把消息从这个 topic 取出来存入数据库。
在时间到了之后再放入到 Kafka 里,以便真正的消费者去执行真正的业务逻辑。
 

 

消息的持久化:

  • rabbitmq

一旦被消费了就无法找回
  • Kafka

持久化一个专门的日志文件
 
 
消息的吞吐量:
Kafka 是每秒几十万条消息吞吐,而 RabbitMQ 的吞吐量是每秒几万条消息。
posted @ 2022-08-05 22:15  雨落知音  阅读(282)  评论(0编辑  收藏  举报