导语
RabbitMQ 整体是一个与其他中间件相同的模型,主要是负责接收、存储和转发消息。可以把消息传递的过程想想成快递:将一个快递送到快递公司,快递公司由快递员送到收件人的人中,RabbitMQ就类似于一个快递公司,也就是RabbitMQ的交换机模型。
RabbitMQ 整体架构模型
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e7a754154deac1e670a3a89849d0182f.png)
生产者和消费者
Producer :生产者,就是消息投递方
生产者创建消息之后,发布到RabbitMQ中,这个消息可以分为两个部分:消息体和标签(Label)。消息体也称为payload,在实际中,消息体一般是带有业务逻辑结构的数据,例如一个JSON串,或者是XML字符串。当然可以进一步对这个消息进行序列化操作。消息的标签来描述这个消息,例如一个交换器的名称和一个路由键。生产者把消息交给RabbitMQ,RabbitMQ会把消息路由到Consumer中。
Consumer :消费者,接收消息一方
消费者连接到RabbitMQ 服务器,并订阅消息队列上的消息,当消费者消费一条消息时,只是消费消息的消息体(payload)。在消息路由的过程中,消息的标签会被丢弃,存入到队列中的消息只有消息体,消费者也只会有消息体的数据,也就是并不知道生产者是哪个。当然从之前的分享中也知道,消费者也不需要知道生产者是谁。
Broker :消息中间件的服务节点
对于RabbitMQ 来说,一个RabbitMQ Broker 可以简单的看作是一个服务节点或者RabbitMQ服务实例,大多数的情况下可以将一个RabbitMQ Broker 看作一台RabbitMQ 服务器整个的消息流转过程。
- 1、消息生产者将消息序列化封装,封装成消息之后,发送到Broker
- 2、消费者接受到消息之后进行反序列化成被消费系统需要的数据
消息队列
Queue :队列
队列作为RabbitMQ的内部对象,用于存储消息,RabbitMQ中消息只能存储到队列中,这个与Kafka中间件消息存储方式有所不同,Kafka是将消息存储在Topic的逻辑层,通过分区索引的方式进行存储,队列中存储的就是实际存储消息的位移标识。RabbitMQ 的生产者生产的消息最终被投递到了队列中,消费者可以从队列中获取消息并且进行消费。
多个消费者可以订阅同一个队列消息,当然这个队列的消息也会被通过轮询的方式进行平均分配,给多个相同逻辑的消费者进行消费处理,并不是每个消费者都收到所有的消息并进行处理。如下图所示
RabbitMQ不支持队列层面的消息广播消费,如果需要进行广播,则需要在队列基础上进行二次的开发,处理逻辑也会被原来的处理逻辑更加复杂。
交换器、路由键、绑定
Exchange :交换器
在生产者产生消息之后并不是直接交给消息队列中,而是将消息发送到Exchange中,由交换器将消息路由到一个或者多个队列中,如果路由没有找到或者返回给生产者,或者会直接将消息丢弃调。这里只是简单地将交换器看作一个应用实体。
RabbitMQ 中交换器的类型有四种,不同的交换器有这不同的路由策略,将在后面的笔记中介绍
RoutingKey:路由键
生产者将消息发给交换器的时候,一般会指定一个RoutingKey,用来指定这个消息的路由规则,而这个RoutingKey需要和交换器的类型和绑定键(BindingKey)联合使用才能最终生效。交换器类型和绑定键在一定情况下用来执行消息的流向。
Binding:绑定
RabbitMQ 中通过绑定将交换器与队列进行关联,在绑定的时候回指定一个绑定键,这样就可以知道正确的消息路由队列如何。
生产者将消息发送给交换器的时候,需要一个RoutingKey,当BindingKey和RoutingKey相互匹配的时候,消息会被路由到对应的队列中,在绑定多个队列到同一个交换器的时候,这些绑定允许使用相同的BindingKey。BindKey并不是在所有的场景中都可以生效,它依赖于对应的交换器类型,例如fanout类型的交换器就会忽视BindingKey,而是将消息路由到所有绑定的交换器队列中。
在有些场景下其实可以将RoutingKey和BindingKey看作是同一个东西。
交换器类型
RabbitMQ常用的交换器类型有fanout、direct、topic、headers这四种。AMQP协议里还有另外的两种,System和自定义。
fanout
它会把所有转发到该交换器的消息全部路由到所有与该交换器绑定的队列上,有点像是广播,但是实际上并不是。
direct
direct 类型的交换器路由规则相对比较简单,是一种一一对应的关系进行路由。将BindingKey和RoutingKey完全匹配的队列进行路由。如图所示
如果在发送消息的时候设置路由键为info或者debug,消息只会路由到Queue2,如果其他消息则不会被路由到这个队列中。
topic
上面提到的direct类型的路由交换器规则是完全匹配BindingKey和RoutingKey,但是这种严格的匹配在很多的情况下并不能满足实际业务需求,topic 类型的交换器在匹配规则上进行了扩展,它与direct类型的交换器相似,也是将消息路由到BindingKey和RoutingKey相匹配的队列中,但是用法有些不同。
- RoutingKey 为一个“.” 分隔的字符串例如“com.rabbitmq.client”等
- BindingKey和RoutingKey 也是通过 “.”来分隔的字符串
- BindingKey 可以存在两种特殊的字符串“*” 和“#” ,用于做模糊匹配,其中#匹配一个 * 匹配所有
- 路由键为com.rabbitmq.client 消息会同时路由到Queue1和Queue2
- 路由键为com.hidden.client 消息只会被路由到Queue2
- 路由键为com.hidden.demo 消息只会被路由到Queue2
- 路由键为java.rabbitmq.demo 消息只会被路由到Queue1中
- 路由键为java.util.concurrent 的消息就会被丢弃,应为没有设置任何的路由键
header
header 类型的交换器不依赖与路由键的匹配规则来路由消息,而是根据发送消息的内容headers属性进行匹配,在绑定队列和交换器时制定一组键值对,发送消息到交换器的时候RabbitMQ回获取该消息的headers。对比其中的键值对是否匹配队列和交换器绑定的键值对,如果不匹配则不会进行路由,但是这种交换器的性能相对较差、不适用,基本在实际工作中不会用到。
总结
上面的内容将RabbitMQ中的一些重要的概念做了介绍,涉及到了很多的重要的概念,虽然特别抽象,但是作为后期的调优的重要知识,也是需要掌握的。