本文共 4309 字,大约阅读时间需要 14 分钟。
此篇章主要用来讲rabbitMQ的一些知识点,不涉及代码。
1.消息基于什么传输?
TCP连接的创建和销毁的开销都较大,且并发数受系统资源限制,会造成性能瓶颈,RabbitMQ使用信道的方式来传输数据,信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有现在。
2.RabbitMQ上的一个queue中存放的message是否有数量限制?
可以认为是无限制的,取决于机器的内存,但是当一个queue中的消息过多的时候,会造成性能问题。
3.如何保证消息可以正确地发送至Broker中
使用MQ的发送确认方式,可以确保消息正确地发送到Broker中
1.发送方确认模式:将信道设置成confirm模式(发送确认模式),则所有发送至broker的消息都会在到达broker后,或者被持久化到磁盘后,信道会发送一个确认给生产者(包含消息的唯一id),如果消息没有到达broker,则会返回未确认
发送方确认模式是异步的,生产者应用程序在等待确认的同时,,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。
向不存在的 exchange 发 publish 消息会发生什么?向不存在的 queue 执行 consume 动作会发生什么?
会出现404,导致发送失败,不过这个时候可以使用returnCallback,这个接口可以返回下面的东西
这个时候我们可以里面的信息来进行重新发送,但是returnCallback只有在交换机不存在或者queue不存在的时候,才会回调。其它失败的方法可以采用
4.什么情况下会出现黑洞?(blackholed)
此问题指的是 想exchange 投递了message,而由于各种原因导致该message丢失,而消息的发送者却不知道(这就非常可怕了)
出现此问题的场景为
此种问题刚好可以用returncallback来解决哈哈
5.绑定key的最大长度为255字节
6.消息路由的方式
消息路总体来说可以分为3部分:交换器,路由,绑定。
上面文章中笔者以画图的方式画出了,消息路由的流程,这里以文字说明
6.1生产者把消息发送到交换机上
6.2绑定key决定此消息会到那个队列中
如果一个路由key绑定了两个队列,那么生产者在发送给交换器的时候,会将消息发送到两个队列中。
6.3 消息然后到达队列,然后被消费者接收。
详细来说,就是:
7.如何保证消息接收方消费了消息,r
使用消息接收方消息确认机制。
此机制为:消费者接收每一条消息,在处理完逻辑后,b必须手动确认,只有手动确认成功后,broker才会将消息从队列中删除
因为RabbitMQ的消费者机制默认为自动机制,但是自动机制会出现一系列的问题比如,当消费者消费者逻辑已经走完了,但是在自动提交的时候,消费者挂掉了,那么broker会主观的认为,此消费者没有消费能力了,会将此条消费发送给其它绑定此queue的消费者,为什么会有其他消费者呢,因为我们在实际的生产环境中,一个队列有的时候一个消费者压力太大,那样会造成消息积压,我们这时候的处理方法就是使用多个消费者共同绑定此队列,来确保消息被消息的速度。
8.还是沿用上面的说法,既然消费事变会发给其他消费者,那我们有的时候就算使用自动确认,但是还是会失败呀。这个时候我们要引用幂等性这个概念
如何防止消费者重复消费?
我们在发送消息的时候可以指定
CorrelationData correlationData = new CorrelationData(properties.get("number").toString());
或者使用一个参数(唯一id,如果是分布式系统要考虑此id的唯一性,可以使用uuid+时间戳or使用雪花算法声明分布式唯一id)来确保消费者幂等性 具体做法如下
消费者消费成功后再redis或者db中写入这个订单id,然后在消费者前先查询是否有这个订单id,如果有就说明已经消费了,就不用再次消费了。
9.消息如何分发
MQ内部采用的round-robin方式发送给消费者,每条消息,只会发送给一个消费者。
10.MQ有几种消费模式?
pull和push,一种是监听队列,当此队列上有消息时候会自动的推送至消费者中消费
一种是手动去pull,这种也可以认为是MQ中下层消费者限流的一种方式
11.是否该对消息进行持久化
消息的持久化,那么吞吐量必然会下降,并且消息持久化的时候还要使用exchange,queue都要持久化,否则单独
消息持久化没有意义,所以持久化的时候一定要注意非特别重要的业务不要使用持久化,生产环境使用的时候可以采用集群镜像队列的时候,就可以避免单机borker挂掉后的消息丢失情况。
12 RabbitMQ允许发送的最大Message的消息体有64bit指定。
13.如何确保消息不丢失
这里只能采用消息持久化了,MQ的持久化的机制将交换器/队列的 durable 属性设置为 true ,表示交换器/队列是持久交换器/队列,在服务器崩溃或重启之后不需要重新创建交换器/队列(交换器/队列会自动创建)
如果消息想要从 RabbitMQ 崩溃中恢复,那么消息必须:
RabbitMQ 确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件。
14.死信队列
简称DLX,Dead-Letter-Exchange,利用DLX可以达到消息的延时消费。
消息到达死信交换机,可以重新发布到死信队列,继续消费。
队列通过下面3种条件会变成死信队列
* 消息被拒绝(basic.reject/basic.nack(表示拒绝确认消费,这种情况出现在消费的时候出现异常,可以将消息发送到死信队列中从新消费。))
* 消息TTL过期(延时消费,经典实用场景)
* 队列达到最大长度(这个最大值可以自己设置)
15.MQ如何实现高可用
基本上都是基于主从模式的
*.单机模式
*.普通集群模式
*.镜像模式
单机模式:就是指启动单个MQ节点。
普通集群,意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。
而且如果那个放 queue 的实例宕机了,会导致接下来其他实例就无法从那个实例拉取,如果你开启了消息持久化,让 RabbitMQ 落地存储消息的话,消息不一定会丢,得等这个实例恢复了,然后才可以继续从这个 queue 拉取数据。
所以这个事儿就比较尴尬了,这就没有什么所谓的高可用性,这方案主要是提高吞吐量的,就是说让集群中多个节点来服务某个 queue 的读写操作。
镜像集群模式(真正的高可用)
跟普通集群模式不一样的是,在镜像集群模式下,你创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,就是说,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据的意思。然后每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。
这样就不会出现上图中的缺点了
开启镜像模式呢需要在MQ的后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建 queue 的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。
好处在于集群中的某一个节点宕机的时候,消费者还可以通过其他节点的镜像来继续读取消息,缺点也是有 的1.就是将消息同步到所有镜像队列中网络开销不小,2.对动态扩展支持不好,没有kafka的强大(添加即用)
16.MQ的Broker中不知为何存储了1000w条消息
1.查看消费者是否出现问题有问题就修复
2.临死创建多个consumer,保证消费速度迅速消化完这些消息(可放在docker中)
17.如何解决一个热点队列的压力过大问题
在一些大并发情况下直接使用不同的消费队列来保证同份逻辑执行即可(创建多个生产者和消费者,但是代码执行逻辑一样,发送的时候使用取余算法,来保证热点队列将消息发送到不同的队列中。)这也属于将大队列变成多个小队列的思路
18.MQ如何解决分布式事务
19.rabbitMQ如何实现延时消费
两种方法
1.使用死信队列,设置消息的过期时间,消费过期之后,会自动发向死信队列
2.通过插件的方式使用Delayed可以达到延时发送的效果,实现比死信队列简单。
-------------------本文知识储备均来自于蚂蚁课堂,感谢余总的指点