什么是rabbitmq?
rabbitmq是基于AMQP协议的消息队列
什么是AMQP 0-9-1?
AMQP 0-9-1是高级消息队列协议,是一种消息传递协议,它使符合要求的客户端应用程序能够与符合要求的消息传递中间件代理进行通信
rabbitmq概念
名称 | 含义 |
---|---|
Broker | 代理消息的服务器,具体指的是消息队列服务器实体 |
Virtual Host | 虚拟主机,对于多租户共享的服务器,为了实现用户间隔离彼此互不影响,broker里可以开设多个Virtual Host,用作不同用户的权限分离,相当于namespace命名空间 |
Connection | client(publisher/consumer)和broker之间的TCP连接。断开连接的操作只会在client进行,Broker不会断开连接,除非出现网络故障或broker服务出现问题 |
channel | 消息通道,在Connection里,可建立多个channel,每个channel代表一个会话任务,channel之间彼此隔离,client和broker通过channel id识别channel |
Exchange | 交换机,使消息按照指定规则,路由到具体队列 |
Queue | 消息队列,存放消息的载体,等待消费者取出 |
Binding | 绑定,把exchange和queue按照路由规则绑定起来 |
Routing Key | 路由关键字,exchange根据这个关键字进行消息投递 |
producer | 消息生产者,就是创建投递消息的程序 |
consumer | 消息消费者,就是获取消息的程序 |
工作流程
1. publisher请求创建一个Connection,连接到Broker,打开一个channel
2. publisher声明一个exchange,并设置相关属性
3. publisher声明一个queue,并设置相关属性
4. publisher使用routing key,在exchange和queue之间建立好绑定关系
5. publisher投递消息到exchange
6. exchange通过routing key和绑定关系,将消息投递到queue
7. queue将消息分发给consumer
8. consumer获取到消息后进行消息确认或处理完成后消息确认,queue删除消息
实际应用中,声明exchange和queue和Binding应该是由consumer来做,因为可能consumer先行用阻塞获取消息的方式连接到queue,publisher在需要的时候才向consumer声明的exchange投递消息
交换机类型
交换机类型 | 预声明的默认名称 |
---|---|
Direct exchange(直连交换机) | (Empty string) and amq.direct |
Fanout exchange(扇型交换机) | amq.fanout |
Topic exchange(主题交换机) | amq.topic |
Headers exchange(头交换机) | amq.match (and amq.headers in RabbitMQ) |
交换机属性
属性名 | 含义 |
---|---|
Name(名称) | 交换机名称 |
Durability(持久) | Broker重启后,交换机是否还存在 |
Auto-delete(自动删除) | 当所有与之绑定的消息队列都完成了对此交换机的使用后,删掉它 |
Arguments(参数) | 可选,由插件和特定于代理的功能使用 |
直连交换(单播)
队列通过路由键routing key(如k=R)绑定到交换机
当有新消息投递到直接交换时,如果k=R,交换机将其投递到使用k=R绑定的队列
扇形交换(广播)
扇形交换将消息路由到绑定到它的所有队列,并忽略routing key
主题交换(多播)
主题交换基于消息路由键routing key,与交换机和队列间的绑定routing key进行匹配,将消息路由到一个或多个队列,主题交换通常用于实现类似各种发布/订阅模式。主题交换通常用于消息的多播路由
头交换机(多播)
头交换机通过消息的headers属性和与交换机绑定的routing key进行匹配,将消息路由到一个或多个队列,当"x-match"设置为“any”时,消息头的任意一个值被匹配就可以满足条件,而当"x-match"设置为“all”的时候,就需要消息头的所有值都匹配成功。
队列
队列存储着即将被应用消费掉的消息。队列跟交换机共享某些属性,但是队列也有一些另外的属性。
名称 | 含义 |
---|---|
Name(名称) | 队列名称 |
Durable(持久) | Broker重启后,队列是否继续存在 |
Exclusive(独占) | 仅由一个连接使用,当该连接关闭时将删除队列 |
Auto-delete(自动删除) | 当最后一个consumer断开连接后自动删除 |
Arguments | 可选参数 |
队列名称
队列名称可以由client(publisher/consumer)定义,也可以由Broker定义,当需要Broker定义队列名称时,队列名称传空字符串即可,后续操作时队列名称也需传空字符串,队列的名字为utf-8编码, 最长不超过255字节,队列名称以“amq”开头的是保留名称,供Broker内部使用,强行使用将返回403错误码
队列持久
持久队列存储到磁盘,因此可以在Broker重新启动后继续运行。不持久的队列称为暂存队列。并非所有场景都要求队列持久。
绑定
绑定是交换机将消息路由给队列所遵循的规则
消息确认
当队列接到consumer的消息确认时将删除消息,消息确认分为两种:
1.consumer获取消息后自动确认
2.consumer获取消息后手动确认,或处理完成后手动确认
拒绝消息
consumer获取消息后如果处理失败,应该通知队列此消息被拒绝,并通知队列该消息是删除还是继续存放在队列,当队列只有一个consumer时,继续存放队列可能会造成队列和消费者间的死循环
预读取消息
对于存在多个consumer的队列,在consumer确认消息之前,设置可以向consumer分发消息的数量,可以简单的实现类似复杂均衡
消息属性
属性名 | 含义 |
---|---|
Content type | 内容类型 |
Content encoding | 编码 |
Routing key | 路由键 |
Delivery mode | 投递模式(持久化 或 非持久化) |
Message priority | 消息优先级 |
Message publishing timestamp | 发布时间戳 |
Expiration period | 消息有效期 |
Publisher application id | Publisher的ID |
RabbitMQ是一个在AMQP基础上实现的企业级消息系统。何谓消息系统,就是消息队列系统,消息队列是“”消费-生产者模型“”的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。
what?消费-生产者模型?对,没错!就是大学操作系统课程里面的“消费者-生产者模式”,记得当时被这个问题坑的不轻啊。
在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步操作,而这种异步处理的方式大大的节省了服务器的请求时间,从而提高了系统的吞吐量。而且不影响服务器做其他相应,不独占服务器资源。
如:注册用户这种服务,它可能解耦成好几种独立的服务(账号验证,邮箱验证码,手机短信码等)。它们作为消费者,等待用户输入数据,在前台数据提交之后会经过分解并发送到各个服务所在的url,分发的那个角色就相当于生产者。消费者在获取数据时候有可能一次不能处理完,那么它们各自有一个请求队列,那就是内存缓冲区了。做这项工作的框架叫做消息队列。
又比如:电商系统中的订单处理系统,传统处理模式是:下订单的时候,订单系统可能会调用库存系统的接口,这样两个系统之间存在一个严重依赖关系,如果库存系统宕机,那么整个流程都会受到影响。现在大多公司的处理方法是:引入消息队列,下完订单,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
对库存系统来说,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。这样实现了两个系统间的解耦。
即使在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。
几个概念说明:
Broker:简单来说就是消息队列服务器实体。
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
producer:消息生产者,就是投递消息的程序。
consumer:消息消费者,就是接受消息的程序。
channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
消息队列的使用过程大概如下:
(1)客户端连接到消息队列服务器,打开一个channel。
(2)客户端声明一个exchange,并设置相关属性。
(3)客户端声明一个queue,并设置相关属性。
(4)客户端使用routing key,在exchange和queue之间建立好绑定关系。
(5)客户端投递消息到exchange。
exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
exchange也有几个类型,完全根据key进行投递的叫做Direct交换机,例如,绑定时设置了routing key为”abc”,那么客户端提交的消息,只有设置了key为”abc”的才会投递到队列。对key进行模式匹配后进行投递的叫做Topic交换机,符号”#”匹配一个或多个词,符号”*”匹配正好一个词。例如”abc.#”匹配”abc.def.ghi”,”abc.*”只匹配”abc.def”。还有一种不需要key的,叫做Fanout交换机,它采取广播模式,一个消息进来时,投递到与该交换机绑定的所有队列。
RabbitMQ支持消息的持久化,也就是数据写在磁盘上,为了数据安全考虑,我想大多数用户都会选择持久化。消息队列持久化包括3个部分:
(1)exchange持久化,在声明时指定durable => 1
(2)queue持久化,在声明时指定durable => 1
(3)消息持久化,在投递时指定delivery_mode => 2(1是非持久化)
如果exchange和queue都是持久化的,那么它们之间的binding也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。