rabbitMq的组件与原理,exchange四种方式,重复消费、顺序错乱

8 篇文章 0 订阅

rabbitMq系统架构

系统架构图如下:

几个概念说明: 
Producer:消息生产者,就是投递消息的程序.
Consumer:消息消费者,就是接受消息的程序. 
message:消息体,根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输 

Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:存放message的对列,每个消息都会被投到一个或多个队列。 

Broker:接收和分发消息的应用,一个RabbitmqServer就是一个broker。
vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。 
Channel:消息通道,在客户端的每个tcp连接里,可建立多个channel,为了节省tcp连接。

Binding:绑定,将Exchange和Queue绑定的时候会指定一个绑定键(BindingKey),因为exchange可以和多个queue绑定,那么消息到达exchange时会带有Routingkey,exchange就根据routingkey和bindingkey是否匹配决定将message路由到一个或多个queue。
BindingKey:绑定键,将Exchange和Queue绑定的时候会指定一个绑定键(BindingKey)
RoutingKey:消息路由键,Producer发送消息给Exchange时会指定RoutingKey,当该消息的RoutingKey和BindinggKey 相匹配时,消息会被路由到对应的队列中

消息生产者并没有直接将消息发送给消息队列,而是通过建立与Exchange的Channel,将消息发送给Exchange,Exchange根据规则,将消息转发给指定的消息队列。消费者通过建立与消息队列相连的Channel,从消息队列中获取消息。

这里谈到的Channel可以理解为建立在生产者/消费者和RabbitMQ服务器之间的TCP连接上的虚拟连接,一个TCP连接上可以建立多个Channel。 RabbitMQ服务器的Exchange对象可以理解为生产者发送消息的邮局,消息队列可以理解为消费者的邮箱。Exchange对象根据它定义的规则和消息包含的routing key以及header信息将消息转发到消息队列。

根据转发消息的规则不同,RabbitMQ服务器中使用的Exchange对象有四种,Direct Exchange, Fanout Exchange, Topic Exchange, Header Exchange,如果定义Exchange时没有指定类型和名称, RabbitMQ将会为每个消息队列设定一个Default Exchange,它的Routing Key是消息队列名称。

消息的路由:
消息提供方->路由->一至多个队列
消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
通过队列路由键,可以把队列绑定到交换器上。
消息到达交换器后,RabbitMQ会将消息的路由键与队列的绑定键进行匹配(针对不同的交换器有不同的路由规则);

exchange的四种类型

1、Direct Exchange
一个 Exchange 和多个 Queue 绑定,会把消息路由到 BindingKey 和 RoutingKey 完全匹配的队列中

2、Fanout Exchange
一个 Exchange 和多个 Queue 绑定,会把消息路由到所有与该 Exchange 绑定的 Queue,此时routingKey不发挥作用

3、Topic Exchange
和 Direct Exchange 相似,也是将消息路由到 BindingKey 和 RoutingKey 匹配的 Queue,匹配规则如下:
RoutingKey 为一个由 “.” 分隔的字符串(被“.”分隔开的每一段成为一个单词)
BindingKey 也为一个由 “.” 分隔的字符串
BindingKey 中可以存在两种特殊的字符串 “*” 和 “#”,其中 “*” 用于匹配一个单词,“#”用于匹配 0 个或者多个单词

4、Header Exchange
性能很差,不实用

exchange类型选择

1、publish 一次 consume 一次 :采用 Direct Exchange
2、publish 一次 consume 多次 :采用 Fanout ,也可以采用direct,绑定多个queue使用相同的routingkey.但fanout更佳
2、publish 一次 consume 次数不定 :采用 Topic Exchange

持久化

一个好的消息队列当然需要消息持久化功能,服务宕机,未消费消息不丢失,RabbitMQ持久化分为Exchange、Queue、Message。Exchange 和 Queue 持久化 指持久化Exchange、Queue 元数据,持久化的是自身,服务宕机,Exchange 和 Queue 自身就没有了。Message 持久化 顾名思义 把每一条消息体持久化,服务宕机,消息不丢失

    Durable 持久、Transient 临时,Queue新建类似

原文:https://blog.csdn.net/aa1215018028/article/details/80932242 

rabbitMq消息确认、重复、缺点等问题

1如何确保消息正确地发送至RabbitMQ? 如何确保消息接收方消费了消息?

发送方确认模式:ack+nack:

将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。
一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。
如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(not acknowledged,未确认)消息。
发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。

消费者确认模式:ack+默认断开重发

为了保证数据不被丢失,RabbitMQ支持消息确认机制,为了保证数据能被正确处理而不仅仅是被Consumer收到,那么我们不能采用no-ack,而应该是在处理完数据之后发送ack. 
在处理完数据之后发送ack,就是告诉RabbitMQ数据已经被接收,处理完成,RabbitMQ可以安全的删除它了. 
如果Consumer退出了但是没有发送ack,那么RabbitMQ就会把这个Message发送到下一个Consumer,这样就保证在Consumer异常退出情况下数据也不会丢失. 
RabbitMQ它没有用到超时机制.RabbitMQ仅仅通过Consumer的连接中断来确认该Message并没有正确处理,也就是说RabbitMQ给了Consumer足够长的时间做数据处理。 
如果忘记ack,那么当Consumer退出时,Mesage会重新分发,然后RabbitMQ会占用越来越多的内存.
下面罗列几种特殊情况:
如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。

2.如何避免消息重复投递、消息的幂等性、消息的顺序?
在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重的依据(消息投递失败并重传),避免重复的消息进入队列;      笔者问题:怎么保证消息相同时inner-msg-id一样,消息不同时inner-msg-id不一样?

消息重复消费的幂等性
1 业务自身具有幂等性或者处理的幂等性,此时消息重复消费没有问题
2 每个消息均有唯一的id,消费消息的时候成功的同时记录该id到库或者redis中,每次消费前进行验证

消息的顺序性
正常订单状态:1订单创建 2订单确认 3待收货 4订单完成 5退货完成
场景:自提商品,为了保证流程完整性,直接走完订单确认、收货、订单完成
每个状态对应一个消息,那么可能出现的问题是234三个消息几乎同时进入mq,但是放到了不同的server上,
假设server3上累积10000条消息,状态3对应的消息存入了server3;server4上累积10条消息,状态4对应的消息存入了server4
因为server4上消息少,所以消费的时候,先消费 订单完成 的消息,然后再消费 待收货 对应的消息,那么最后订单状态变成了待收货,其实已经完成了,有问题。

有两种方案:
1 调整业务处理逻辑,消费消息的逻辑应该是订单的状态只能正向走,不能逆向走,那么即使获取到的消息顺序有问题,订单状态最后也是正常的。
2 将同一个系列相关的消息放到同一个redis的queue中,那么消息就会是顺序的

3.消息基于什么传输?
由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。
信道和tcp连接的区别
信道的原理是一条线程一条信道,多条线程多条信道共同使用一条TCP连接。一条TCP连接可以容纳无限的信道,即使每秒成千上万的请求也不会造成性能瓶颈

4.消息如何分发?
若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。
通过exchange的fanout模式可实现多消费的功能

5.如何确保消息不丢失?
消息持久化,当然前提是队列必须持久化
RabbitMQ确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件,当发布一条持久性消息到持久交换器上时,Rabbit会在消息提交到日志文件后才发送响应。
一旦消费者从持久队列中消费了一条持久化消息,RabbitMQ会在持久化日志中把这条消息标记为等待垃圾收集。如果持久化消息在被消费之前RabbitMQ重启,那么Rabbit会自动重建交换器和队列(以及绑定),并重新发布持久化日志文件中的消息到合适的队列。

6.rabbitmq的集群
镜像集群模式
你创建的queue,无论元数据还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。
好处在于,你任何一个机器宕机了,没事儿,别的机器都可以用。坏处在于,第一,这个性能开销也太大了吧,消息同步所有机器,导致网络带宽压力和消耗很重!第二,这么玩儿,就没有扩展性可言了,如果某个queue负载很重,你加机器,新增的机器也包含了这个queue的所有数据,并没有办法线性扩展你的queue

7.mq的缺点
引入mq增加系统的复杂性和不稳定性
系统引入的外部依赖越多,越容易挂掉,本来你就是A系统调用BCD三个系统的接口就好了,人ABCD四个系统好好的,没啥问题,你偏加个MQ进来,万一MQ挂了咋整?MQ挂了,整套系统崩溃了。
系统复杂性提高:
硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已

一致性问题:
A系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,咋整?你这数据就不一致了。

借鉴:https://blog.csdn.net/qq_42629110/article/details/84965084 
https://blog.csdn.net/maihilton/article/details/80928661

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值