文章目录
RabbitMQ的结构
- Broker:消息队列服务器实体。
一个或多个 erlang node 的逻辑分组,且node上运行着 RabbitMQ 应用程序
- Routing Key:路由关键字,exchange根据这个关键字进⾏消息投递。
- Queue:消息队列载体,每个消息都会被投⼊到⼀个或多个队列。
queue中存放的消息数量可以认为是⽆限制的,限制取决于机器的内存,但是消息过多会导致处理效率的下降
若该队列⾄少有⼀个消费者订阅,消息将以循环(round-robin)的⽅式发送给消费者。每条消息只会分发给⼀个订阅的消费者(前提是消费者能够正常处理消息并进⾏确认)
- Binding:绑定,它的作⽤就是把exchange和queue按照路由规则绑定起来。
- Exchange:消息交换机,指定消息的路由规则。
exchange 内部为保存 binding 关系的查找表
将交换器/队列的durable属性设置为true,表示交换器/队列是持久交换器/队列,在服务器崩溃或重启之后不需要重新创建交换器/队列(交换器/队列会⾃动创建)。
- channel:消息通道,在客户端的每个连接⾥,可建⽴多个channel,每个channel代表⼀个会话任务。
channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给 queue,中每一个 channel 运行在一个独立的线程上,多线程共享同一个 socket
- vhost:虚拟主机,⼀个broker⾥可以开设多个vhost,⽤作不同⽤户的权限分离。
vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制
- producer:消息⽣产者。
- consumer:消息消费者。
RabbitMQ的优点
这里面大部分其实也是所有消息中间件的优点
- 应用解耦
- 任务异步处理
- 消息分发
- 流量削峰
- 消息缓冲
除此之外相对其他的消息中间件,RabbitMQ还具有以下优势:
- 基于erlang开发,并发能力很强,性能很好
- 管理界面丰富,对中小型企业来说使用管理相对方便
消息队中间件的缺点
- 系统功能依赖于消息队列,消息中间件运行异常将影响系统业务
- 系统复杂性增加,需要考虑的问题更多,比如:消息一致性问题、消息重复消费问题以及死信队列等。
- 系统出现问题的影响因素增加
消息的路由实现
首先,消息路由必须要有三部分:交换器、路由键、绑定关系。
生产者将消息发布到交换器上,再通过路由键决定消息应该发送到哪个队列中。
- 消息发布到交换器时,消息将拥有⼀个路由键(routing key),在消息创建时设定。
- 通过队列路由键,可以把队列绑定到交换器上。
- 消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进⾏匹配(针对不同的交换器有不同的路由规则)。
- 如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进⼊“⿊洞”。
什么是“黑洞”?
即消息莫名其妙地丢失了。
发送者向交换器投递了消息,而由于各种原因导致该消息丢失,但发送者却不知道。
可导致黑洞问题的情况:
1.向未绑定队列的交换器发送消息;
2.交换器以绑定值 key_A 绑定了队列A,但向该交换器发送消息使用的路由值却是 key_B。
如何避免“黑洞”问题?
该问题没有什么效方法,只能在写代码时注意交换器的配置。
另外,如果在执行 Basic.Publish 时设置 mandatory=true ,则在遇到可能出现 blackholed 情况时,服务器会通过返回 Basic.Return 告知当前 message 无法被正确投递(内含原因 312NO_ROUTE)。
RabbitMQ的交换器种类
fanout交换器
它会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中,如果交换器收到消息,将会⼴播到所有绑定的队列上
direct交换器
direct类型的交换器路由规则很简单,它会把消息路由到哪些BindingKey和RoutingKey完全匹配的队列中;
topic交换器
匹配规则⽐direct更灵活,可以使来⾃不同源头的消息能够到达同⼀个队列。 使⽤topic交换器时,可以使⽤通配符,⽐如:“*” 匹配特定位置的任意⽂本, “.” 把路由键分为了⼏部分,“#” 匹配所有规则等。特别注意:发往topic交换器的消息不能随意的设置选择键(routing_key),必须是由"."隔开的⼀系列的标识符组成
headers交换器
根据发送消息内容的headers属性进⾏匹配(由于性能比较差,不实⽤)。
如何确保消息不丢失?
RabbitMQ确保持久性消息能从服务器重启中恢复的⽅式是,将它们写⼊磁盘上的⼀个持久化⽇志⽂件,当发布⼀条持久性消息到持久交换器上时,Rabbit会在消息提交到⽇志⽂件后才发送响应(如果消息路由到了⾮持久队列,它会⾃动从持久化⽇志中移除)。⼀旦消费者从持久队列中消费了⼀条持久化消息,RabbitMQ会在持久化⽇志中把这条消息标记为等待垃圾收集。如果持久化消息在被消费之前RabbitMQ重启,那么Rabbit会⾃动重建交换器和队列(以及绑定),并重播持久化⽇志⽂件中的消息到合适的队列或者交换器上。
即交换器和队列durable属性必须是true,这样RabbitMQ的会记录所有设置的持久化信息,包括队列、交换器、持久化消息。
在服务器崩溃或重启之后不需要重新创建交换器/队列(交换器/队列会根据持久日志⾃动创建)
消息是持久化消息(即persistent属性为true),因为如果消息不是持久化消息,那么它就会从持久化日志中被移除,不存在记录,更不用谈被回恢复。
总结来说,如果消息想要从Rabbit崩溃中恢复,那么消息必须满足以下三点:
1、在消息发布前,通过把它的“投递模式”选项设置(持久)来把消息标记成持久化
2、将消息发送到持久交换器
3、消息到达持久队列
顺便一提,在Spring-rabbit当中,我们只需要设置交换器和队列是持久的就可以了,因为消息默认都是持久化消息。
public class MessageProperties implements Serializable {
... ...
//投递模式
public static final MessageDeliveryMode DEFAULT_DELIVERY_MODE;
... ...
static {
//持久
DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;
DEFAULT_PRIORITY = 0;
}
}