channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给 queue 。
消息是如何发送给MQ:
生产者把消息发布到 Exchange 上,消息最终到达队列并被消费者接收,而 Binding 决定交换器的消息应该发送到那个队列
- 消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
- 通过队列路由键,可以把队列绑定到交换器上。
- 消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进入 “黑洞”(消息)
常用的交换器:
direct-exchange: 如果路由器routingKey完全匹配,消息就被投递到相应的队列. 可以理解为1:1
fanout-exchange:如果交换器接收到消息,不用管RoutingKey。将会广播到所有绑定的队列上(所有队列都会绑定到一个exchange上)。可以理解为1:n
topic-exchange: 当消息发送者发送信息到Topic Exchage交换机的时候,这时候发送消息的route key会与binding key进行通配符匹配,所有匹配成功的消息都会发送到queue。routingKey可以有通配符:'*','#'.其中'*'表示匹配一个单词, '#'则表示匹配没有或者多个单词。 发往 Topic 交换器的消息不能随意的设置选择键(routing_key),必须是由 "."
隔开的一系列的标识符组成。 可以理解为n:n。
1.普通集群模式(cluster模式)
在多个机器上启动多个Rabbit实例,每个集群一个。创建的queue,只会在一个RabbitMQ实例上,但是每个实例都同步queue的元数据(queue的一些配置信息,可以找到queue所在的实例),如果consumer连接到了另外一个实例(非 owner queue),那么实例会在queue所在的实例拉取数据。如果放queue的实例宕机了,会导致其他实例无法拉取到该queue的消息。
2.镜像集群模式(mirrored queue模式)
这种才是Rabbit真正的高可用模式。跟普通集群cluster的区别在于,你创建的queue,不论queue的元数据还是queue中的消息都会存在多个实例。就是每个集群实例都有queue的完整镜像(即为副本)。实现了真正的高可用,但是坏处就是性能开销很大。
Rabbit实现数据的可靠性:
Producter如何保证消息的发送成功?
同步事务或者异步confirmCallback模式(投递到broker cluster,确认返回),returnCallback模式(未投递到queue退回模式)
1.事务功能。同步返回结果,消耗性能,吞吐量变小
2.confirmCallback。在生产者开启confirm功能,每次些消息的时候,每个消息分配唯一的id,消息写入broker cluster,broker cluster会回调一个ack消息,确认这个消息OK了。
3.returnCallback。 从 exchange->queue 投递失败则会返回一个 returnCallback
利用这两个 callback 控制消息的最终一致性和部分纠错能力
Broker消息保证可靠不丢失? queue以及消息持久化到磁盘
开始Broker持久化,消息写入之后持久化到磁盘。哪怕Broker挂了,回复之后可以读取到之前存储的数据。
设置持久化的步骤:1.创建queue的时候设置为持久化。 2.发送消息的时候将消息的deliveryMode设置为2.
Consumer如何保证消息消费成功 取消自动ack,消息处理完成手动提交ack
ack机制。关闭Rabbit的自动ack机制。在消费者代码中实现消息处理完成,在程序中调用api提供的ack。
如何实现消息的顺序消费?
不同于Kafak和RocketMQ,Rabbit不存在类似于topic的概念,而是真正的一条条的队列,并且每个queue可以被多个consumer拉取消息。
解决方案:
1.每个queue只有一个consumer,用多个queue来解决
2.一个queue只对应一个consumer,然后在这个consumer内部用队列做排队,然后分发给底层的多个worker来处理。
死信队列(DLX) 当一个消息在一个队列中变成死信时,他被重新publish到另外一个死信队列。消息变成死信的几种情况:
1.消息被消费者拒绝 (basic.reject / basic.nack)并且 requeue=false
2.消息超时
3.队列达到最大长度。