RabbitMq 消息队列
1 、 exchange queue binding-key routing-key概念及相互间的关系
1.queue :存储消息的队列,可以指定name来唯一确定
2.exchange:交换机(常用有三种),用于接收生产者发来的消息,并通过binding-key 与 routing-key 的匹配关系来决定将消息分发到指定queue
Direct(路由模式):完全匹配 > 当消息的routing-key 与 exchange和queue间的binding-key完全匹配时,将消息分发到该queue
Fanout (订阅模式):与binding-key和routing-key无关,将接受到的消息分发给有绑定关系的所有队列(不论binding-key和routing-key是什么)
Topic (通配符模式):用消息的routing-key 与 exchange和queue间的binding-key 进行模式匹配,当满足规则时,分发到满足规则的所有队列
2.如何确保消息正确地发送至RabbitMQ? 如何确保消息接收方消费了消息?如何避免消息重复投递或重复消费?
持久化有两种方式 MQ事物(太耗性能,不使用)与confirm模式。
发送方确认模式:
将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。
一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。
如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(not acknowledged,未确认)消息。
发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。
接收方确认机制
接收方消息确认机制:消费者接收每一条消息后都必须进行确认(手动确认机制)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。
这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。保证数据的最终一致性;
在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重的依据(消息投递失败并重传),避免重复的消息进入队列;
消息幂等性: 在消息消费时,要求消息体中必须要有一个bizId(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重的依据,避免同一条消息被重复消费。
消息持久化,当然前提是队列、交换机必须持久化
①、将queue的持久化标识durable设置为true,则代表是一个持久的队列
②、发送消息的时候将deliveryMode=2
RabbitMQ确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件,当发布一条持久性消息到持久交换器上时,Rabbit会在消息提交到日志文件后才发送响应。
一旦消费者从持久队列中消费了一条持久化消息,RabbitMQ会在持久化日志中把这条消息标记为等待垃圾收集。如果持久化消息在被消费之前RabbitMQ重启,那么Rabbit会自动重建交换器和队列(以及绑定),并重新发布持久化日志文件中的消息到合适的队列。
消息持久化
ACK确认机制
设置集群镜像模式
消息补偿机制
ACK确认机制
设置集群镜像模式
消息补偿机制
![](https://img2018.cnblogs.com/blog/1694759/201906/1694759-20190604183344448-1833278645.png)
3.消息基于什么传输?
由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。
4.消息如何分发?
若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。
通过路由可实现多消费的功能
- 轮询: 默认的策略,消费者轮流,平均地接收消息
- 公平分发: 根据消费者的能力来分发消息,给空闲的消费者发送更多消息
5.消息怎么路由?
消息提供方->路由->一至多个队列
消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
通过队列路由键,可以把队列绑定到交换器上。
消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则);
常用的交换器主要分为一下三种:
fanout:如果交换器收到消息,将会广播到所有绑定的队列上
direct:如果路由键完全匹配,消息就被投递到相应的队列
topic:可以使来自不同源头的消息能够到达同一个队列。 使用topic交换器时,可以使用通配符
6.rabbitmq的集群
普通集群模式:多台机器启动多个实例,节点数据不复制,消息在某一台中,节点间通信,一台挂了 就不可用了。
镜像集群模式:
你创建的queue,无论元数据还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。
好处在于,你任何一个机器宕机了,没事儿,别的机器都可以用。坏处在于,第一,这个性能开销也太大了吧,消息同步所有机器,导致网络带宽压力和消耗很重!第二,这么玩儿,就没有扩展性可言了,如果某个queue负载很重,你加机器,新增的机器也包含了这个queue的所有数据,并没有办法线性扩展你的queue
7、什么是死信呢?什么样的消息会变成死信呢?
消息被拒绝(basic.reject或basic.nack)并且requeue=false.
消息TTL过期
队列达到最大长度(队列满了,无法再添加数据到mq中)
8、如果让你写一个消息队列,该如何进行架构设计?说一下你的思路。
快速扩容, 分布式, 数据持久化, 磁盘顺序读写, 高可用, 消息0丢失(ack)
9、rabbitMQ怎么做的持久化
写入文件前会有一个Buffer,大小为1M(1048576),数据在写入文件时,首先会写入到这个Buffer,如果Buffer已满,则会将Buffer写入到文件(未必刷到磁盘)
有个固定的刷盘时间:25ms,也就是不管Buffer满不满,每隔25ms,Buffer里的数据及未刷新到磁盘的文件内容必定会刷到磁盘
每次消息写入后,如果没有后续写入请求,则会直接将已写入的消息刷到磁盘:使用Erlang的receive x after 0来实现,只要进程的信箱里没有消息,则产生一个timeout消息,而timeout会触发刷盘操作
10、RabbitMQ、kafka之间的比较
1、 RabbitMq比kafka成熟,在可用性上,稳定性上,可靠性上,RabbitMq超过kafka。rabbitMq 延迟度低。
2、 Kafka设计的初衷就是处理日志的,可以看做是一个日志系统,针对性很强,所以它并没有具备一个成熟MQ应该具备的特性(不支持事物)
3、 Kafka的性能(吞吐量、tps)比RabbitMq要强,这篇文章的作者认为,两者在这方面没有可比性。
4.技术上面,使用rabbitMq比kafka 长,两者社区活跃度都高。
5.RocketMQ 阿里出品,社区活跃度不是很高,技术实力强大的公司可选择。
![](https://img2018.cnblogs.com/blog/1694759/201906/1694759-20190604183408323-1527893242.png)
11、RabbitMQ如何实现延时队列?
利用TTL(队列的消息存活时间或者消息存活时间),加上死信交换机
12、如何保证消息的顺序性
拆分多个queue,每个queue对应一个consumer,发消息时指定到某队列;
或者只有一个队列只有一个消费者的情况下才能保证顺序