一.介绍
RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消 息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。
二.引入
基于Queue实现生产者消费者模型
对于RabbitMQ来说,生产和消费不再针对内存里的一个Queue对象,而是在某台服务器上的RabbitMQ Server实现消息队列
实现最简单的队列通信:
三.几种模式
1.work模式
在这种模式下,RabbitMQ会默认把p发的消息依次分发给各个消费者(c),跟负载均衡差不多
消息发送端
消息接受端
启动一个发送端,启动三个接收端,通过生产者多发送几条消息,你会发现,这几条消息会被依次分配到各个消费者身上。
注意:
1)消息不丢失
设置消息应答模式no-ack = False,如果有一个消费者挂掉了,那么我们应该将分发给它的任务交付给另一个消费者去处理。
如果no-ack = True,那么如果杀死正在执行任务的消费者,会丢失正在处理的消息,也会丢失已经分发给这个消费者但尚未处理的消息。
我们已经了解了如何确保即使消费者死亡,任务也不会丢失。但是如果RabbitMQ服务器停止,我们的任务仍将失去!
当RabbitMQ退出或者崩溃,将会丢失队列和消息。除非你不要队列和消息。
所以两件事儿必须保证消息不被丢失:我们必须把“队列”和“消息”设为持久化。
2)接受端和发送端都需要设置durable=true ,其实保证的是队列的持久化
3)要实现消息持久化还需要设置发送端delivery_mode = 2
如果Rabbit只管按顺序把消息发到各个消费者身上,不考虑消费者负载的话,很可能出现,一个机器配置不高的消费者那里堆积了很多消息处理不完,同时配置高的消费者却一直很轻松。为解决此问题,可以在各个消费者端,配置perfetch=1,意思就是告诉RabbitMQ在我这个消费者当前消息还没处理完的时候就不要再给我发新消息了。
带消息持久化+公平分发的完整代码
发送端
接收端
2.发布/订阅模式
之前的例子基本都是1对1的消息发送和接收,即消息只能发送到指定的queue里,但有些时候你想让你的消息被所有的Queue收到,类似广播的效果,这时候就要用到exchange了,
Exchange在定义的时候是有类型的,以决定到底是哪些Queue符合条件,可以接收消息。
发送方:
接收方
应用场景
比如一个商城系统需要在管理员上传商品新的图片时,前台系统必须更新图片,日志系统必须记录相应的日志,那么就可以将两个队列绑定到图片上传交换器上,一个用于前台系统更新图片,另一个用于日志系统记录日志。
3.路由模式
RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。例如:我们只想把error级别的日志写到磁盘文件中,而其它级别的日志消息则过滤掉。
在这里我们将要使用direct类型的exchange。Direct类型exchange的路由算法是很简单的:要想一个消息能到达这个队列,需要binding key和routing key正好能匹配得上。
发送端
接受端:
应用场景
利用消费者能够有选择性的接收消息的特性,比如我们商城系统的后台管理系统对于商品进行修改、删除、新增操作都需要更新前台系统的界面展示,而查询操作确不需要,那么这两个队列分开接收消息就比较好。
4.主题模式
上面的路由模式是根据路由key进行完整的匹配(完全相等才发送消息),这里的通配符模式通俗的来讲就是模糊匹配。
在topic类型下,可以让队列绑定几个模糊的关键字,之后发送者将数据发送到exchange,exchange将传入”路由值“和 ”关键字“进行匹配,匹配成功,则将数据发送到指定队列。
- # 表示可以匹配 0 个 或 多个 单词
- * 表示只能匹配 一个 单词
例子
发送方
接收方
总结:
关于 RabbitMQ 的五种队列,其实实际使用最多的是最后一种主题模式,通过模糊匹配,使得操作更加自如。那么我们总结一下有交换器参与的队列(最后三种队列)工作方式如下
生产者:
1.链接Rabbit MQ--->2.获取信道--->3.声明交换器--->4.创建消息--->5.发布消息--->6.关闭信道--->7.关闭链接
消费者:
1.链接Rabbit MQ--->2.获取信道--->3.声明交换器--->4.声明队列--->5.把队列和交换器绑定-->6.消费消息--->7.关闭信道--->8.关闭链接
四.远程调用(RPC)
前面介绍的都是一个发送方,一个接收方,如果我们发送方发送了之后想从接收方获取结果呢?
首先,发送方发送请求时,声明结果返回的队列(即结果返回时到哪个队列取结果),并在发送消息时将需要返回结果的队列以及生成的corr_id一并发送,
接收方收到请求后,将结果发送到指定的队列,并附带上corr_id。
最后原先的发送方再到指定的队列取结果。
实现一个远程调用斐波那契数列
RPC server
RPC client