消息队列 --- RabbitMQ

https://blog.csdn.net/weixin_37641832/article/details/83270778
https://www.jianshu.com/p/469f4608ce5d
https://blog.csdn.net/jerryDzan/article/details/89183625
https://www.cnblogs.com/haolujun/p/9632835.html
https://www.cnblogs.com/jian0110/p/10389780.html
https://blog.csdn.net/jerryDzan/article/details/89183625

同步调用选用RPC
异步调用选用MQ

RabbitMQ

RabbitMQ 基于AMQP协议采用 Erlang 实现的工业级的消息队列(MQ)服务器。
AMQP协议: https://blog.csdn.net/weixin_37641832/article/details/83270778

AMQP工作过程
1、发布者(Publisher)发布消息(Message),经由交换机(Exchange)。
2、交换机根据路由规则将收到的消息分发给与该交换机绑定的队列(Queue)。
3、最后 AMQP 代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取

加强:
1、发布者、交换机、队列、消费者都可以有多个。同时因为 AMQP 是一个网络协议,所以这个过程中的发布者,消费者,消息代理 可以分别存在于不同的设备上。
2、发布者发布消息时可以给消息指定各种消息属性(Message Meta-data)。有些属性有可能会被消息代理(Brokers)使用,然而其他的属性则是完全不透明的,它们只能被接收消息的应用所使用。
3、消息确认机制:基于网络不可靠原因,AMQP 模块包含了一个消息确认(Message Acknowledgements)机制:当一个消息从队列中投递给消费者后,不会立即从队列中删除,直到它收到来自消费者的确认回执(Acknowledgement)后,才完全从队列中删除。
4、在某些情况下,例如当一个消息无法被成功路由时(无法从交换机分发到队列),消息或许会被返回给发布者并被丢弃。或者,如果消息代理执行了延期操作,消息会被放入一个所谓的死信队列中。此时,消息发布者可以选择某些参数来处理这些特殊情况。

队列
RabbitMQ中消息都只能存储在队列中,当多个消费者订阅一个队列时,队列中的消息会被平均分摊(Round-Robin),也就是并不是每个消费者都能收到所有的消息并处理。

交换器、路由键、绑定 
(1)Exchange(交换机):交换机是用来发送消息的 AMQP 实体。交换机拿到一个消息之后将它路由给一个或零个队列。它使用哪种路由算法是由交换机类型和绑定(Bindings)规则所决定的。
(2) 路由键(RoutingKey):生产者将消息发送给交换器的时候,会指定RoutingKey指定路由规则,实际情况是需要将RoutingKey、交换器类型、绑定键联合使用才能最终生效。当交换器类型与BindingKey固定情况下,通过执行RoutingKey来决定消息流向哪里。
(3)绑定(BindingKey):通过绑定键将交换器与队列关联起来,这样RabbitMQ就知道如何正确地将消息路由到队列,其实绑定键也是一种路由键的一种,不过是用在绑定交换器与队列的时候。

总结有以下三点:
(1) 生产者将消息发送给哪个Exchange(交换器)是需要由RoutingKey(路由键)决定的,生产者需要将Exchange(交换器)与哪个队列绑定时需要由BindingKey(绑定键)决定的(当然还要看交换器类型,BindingKey不一定会生效,如fanout(扇型)类型交换器)。
(2) 生产者将消息发送给交换器时,需要一个RoutingKey,当BindingKey和RoutingKey相匹配时,消息会被路由到对象的队列中(当然也要看交换器类型)。
(3) BindingKey其实也属于路由键的一种,在使用邦定的时候,需要的路由键是BingdingKey,在发送消息的时,需要的路由键是RoutingKey 。

交换机分类:
直连交换机(direct exchange)(默认): 它会把消息路由到BindingKey与RoutingKey完全匹配的队列中。
主题交换机(Topic): 是direct上的扩展,同样是利用RoutingKey与BindingKey相匹配,但是匹配规则不一样,支持模糊匹配,可以将一条消息发送给指定的多个队列上。
在这里插入图片描述
扇型交换机(funout exchange): 它会把交换器上的所有消息发送给绑定在自己身上的队列中,不需要BindingKey生效
头交换机(headers) :首部交换机是忽略routing_key的一种路由方式。路由器和交换机路由的规则是通过Headers信息来交换的,这个有点像HTTP的Headers。这种交换器性能会很差,一般不会使用。

除交换机类型之外,再声明交换机时还可以附带很多其他属性,重要的几个是:
Name:
Durability:消息代理后,交换机是否存在
Auto-delete:当所有与之绑定的消息队列都完成对此交换机的使用后删除
Arguments:依赖代理本身

guest 只能用于locahost访问 新建账号请使用http://localhost:15672/ 在Admin 添加新的账号
RabbitMQ用户角色及权限控制 https://blog.csdn.net/zyz511919766/article/details/42292655

Queue队列:
队列属性
Name
Durable(消息代理重启后,队列依旧存在)
Exclusive(只被一个连接(connection)使用,而且当连接关闭后队列即被删除)
Auto-delete(当最后一个消费者退订后即被删除)
Arguments(一些消息代理用他来完成类似与 TTL 的某些额外功能)

队列创建: 队列在声明(declare)后才能被使用。如果一个队列尚不存在,声明一个队列时会创建它。如果声明的队列已经存在,并且属性完全相同,那么此次声明不会对原有队列产生任何影响。如果声明中的属性与已存在队列的属性有差异,那么一个错误代码为 406 的通道级异常就会被抛出。

队列持久化
持久化队列(Durable queues)会被存储在磁盘上,当消息代理(broker)重启的时候,它依旧存在。没有被持久化的队列称作暂存队列(Transient queues)。并不是所有的场景和案例都需要将队列持久化。
持久化的队列并不会使得路由到它的消息也具有持久性。倘若消息代理挂掉了,重新启动,那么在重启的过程中持久化队列会被重新声明,无论怎样,只有经过持久化的消息才能被重新恢复。

消息机制
消息确认:
1)自动确认模式:当消息代理(broker)将消息发送给应用后立即删除。(使用 AMQP 方法:basic.deliver 或 basic.get-ok))
2)显式确认模式:待应用(application)发送一个确认回执(acknowledgement)后再删除消息。(使用 AMQP 方法:basic.ack)

Rabbit集群搭建: https://www.sundayle.com/rabbitmq-cluster-build/

Rabbit面试题:

(1)使用RabbitMQ有什么好处?
1.解耦,系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦!
2.异步,将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度
3.削峰,并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常
(2)RabbitMQ 上的一个 queue 中存放的 message 是否有数量限制?
答:可以认为是无限制,因为限制取决于机器的内存,但是消息过多会导致处理效率的下降。
(3) RabbitMQ 概念里的 channel、exchange 和 queue 是逻辑概念,还是对应着进程实体?分别起什么作用?
channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给 queue 。
exchange 内部实现为保存 binding 关系的查找表;
queue 具有自己的 erlang 进程;
(4)消息基于什么传输?
RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制
(5)什么是元数据?元数据分为哪些类型?包括哪些内容?与 cluster 相关的元数据有哪些?元数据是如何保存的?元数据在 cluster 中是如何分布的?
在非 cluster 模式下:
元数据主要分为 Queue 元数据(queue 名字和属性等)、Exchange 元数据(exchange 名字、类型和属性等)、Binding 元数据(存放路由关系的查找表)、Vhost 元数据(vhost 范围内针对前三者的名字空间约束和安全属性设置)。
在 cluster 模式下;
还包括 cluster 中 node 位置信息和 node 关系信息。元数据按照 erlang node 的类型确定是仅保存于 RAM 中,还是同时保存在 RAM 和 disk 上。元数据在 cluster 中是全 node 分布的。
(6)如何避免消息重复投递或重复消费?
在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列;在消息消费时,要求消息体中必须要有一个bizId(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重和幂等的依据,避免同一条消息被重复消费。

(7)如何解决丢数据的问题?
1.生产者丢数据
RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。
transaction机制就是说,发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。然而缺点就是吞吐量下降了。生产上用confirm模式的居多。一旦channel进入confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,rabbitMQ就会发送一个Ack给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了.如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。
2.消息队列丢数据
处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。
3.消费者丢数据
启用手动确认模式可以解决这个问题
(8)如何避免消息重复投递或重复消费?
MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列;
在消息消费时,要求消息体中必须要有一个bizId(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重和幂等的依据,避免同一条消息被重复消费。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值