一、基本概念
RabbitMQ是一个开源的遵循AMQP协议实现的基于Erlang语言编写,支持多种客户端(语言)。用于在分布式系统中存储消息,转发消息,具有高可用,高可扩性,易用性等特征。
官网:https://www.rabbitmq.com/
二、安装
此文章只记录Docker安装方式
// 获取镜像
docker pull rabbitmq:management
// 创建运行容器
docker run -di --name myrabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management
运行成功后,浏览器访问http://ip:15672/ 查看是否启动成功
此处需注意,如果使用的是阿里云等云服务器,请别忘记开放端口
三、核心概念
Broker :也就是安装的rabbitmq服务
Connection:连接,应用程序与Broker的网络连接 TCP/IP
Channel:网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道,客户端可以建立对各Channel,每个Channel代表一个会话任务。
Message:消息,服务与应用程序之间传送的数据,由Properties和body组成,Properties可是对消息进行修饰,比如消息的优先级,延迟等高级特性,Body则就是消息体的内容。
Virtual Host:虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机理由可以有若干个Exhange和Queueu,同一个虚拟主机里面不能有相同名字的Exchange
Exchange:交换机,接受消息,根据路由键发送消息到绑定的队列,所有消息的均是投递给交换机,最终由交换机转发给队列!!!
Bindings:Exchange和Queue的绑定关系,绑定后,交换机转发消息的时候才能转发到已绑定的队列中。
Routing key:是一个路由规则,虚拟机可以用它来确定如何路由一个特定消息,简单来说就是将消息转发给队列的时候进行一下判定,符合此路由的才进行转发,不符合的就不转发。
Queue:消息队列,保存消息,然后由消费者监听进行消费
四、工作模式
图片来自官网:https://www.rabbitmq.com/getstarted.html
就挑下面几种进行解释:
4.1.简单模式
简单模式顾名思义,很简单,发布者,队列,消费者,发布者发送后直接交给默认交换机,交换机转发给队列,然后消费者消费
4.2.fanout模式
fanout也就是最简单的发布订阅模式,此模式将N个队列绑定此模式的交换机后,交换机直接会将消息全部转发给所绑定队列,哪怕你设置了RoutingKey也不生效
4.3.direct模式
direct也是一种发布订阅模式,只不过添加了RoutingKey的概念,你可以将队列绑定交换机的时候指定好对应的RoutingKey,那么交换机转发消息的时候会去判断发送消息所携带的RoutingKey是否与队列绑定的RoutingKey匹配,匹配才会转发到对应的队列
4.4.topic模式
topic是对direct模式的增强版,也就是对RoutingKey进行了增强,增加了模糊匹配的功能,如有两个队列,a队列绑定的RoutingKey设置为*.topic.#,b队列绑定的为#.topic.**,此处注意 * 代表有且仅有一个且必须得有一个,#代表是可有可无,可以有多个也可以一个没有,如果发布者发送的消息绑定的key为topic.test,那么这条消息只会转发给b队列,因为a队列的key前面匹配符为*,那么也就是要求必须拥有一个,没有则不匹配,不进行转发,如果发送者发送的消息绑定的key为test.topic,那么相应的也就是转发给a队列,而不转发给b队列,如果绑定的为test.topic.test,那a,b队列均可收到
4.5.Work模式
当一个队列有多个消费者监听消费的时候,该如何进行分发?
1.平均主义 : 平均主义就是不管消费者的消费能力,一人一条
2.能者多劳 : 能者多劳就是你消费的快,你就多消费几条,能者多老的实现的方式就是设置手动应答
五、高级特性
5.1.TTL(过期时间)
TTL可以对消息设置过期时间,在过期时间内可以被消费者获取;超过期限自动被删除。RabbitMQ可以对消息和队列设置TTL。
第一是可以通过队列属性设置,队列中所有消息都有相同的过期时间。 第二是可以对消息进行单独设置,每条消息TTL可以不同。
如果两种方法同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,可以设置死信队列,那么过期的消息可以投递到死信队列,消费者将无法再收到该消息。
5.2.死信队列
死信队列其实就是普通队列,只不过是就是队列的一种属性,当队列有异常投递的消息时,会将消息转发到此队列,所以又被称为死信队列,也就是死了的信息,要想使用死信队列,只需要在定义队列的时候设置队列参数 x-dead-letter-exchange 指定交换机即可。 消息变成死信,可能是由于以下的原因:
1.消息被拒绝
2.消息过期
3.队列达到最大长度
5.3.消息确认机制
server:
port: 8080
spring:
rabbitmq:
username: "test"
password: "test"
virtual-host: /
host: ip
port: 5672
publisher-confirm-type: correlated
// publisher-confirm-type有NONE、CORRELATED、SIMPLE几种值
// NONE值是默认值,禁用发布确认模式
// CORRELATED值是开启发布确认模式,成功提交到交换器后会触发回调方法
// SIMPLE值经测试有两种效果,其一效果和CORRELATED值一样会触发回调方法,其二在发布消息成功后使用rabbitTemplate调用waitForConfirms或waitForConfirmsOrDie方法等待broker节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是waitForConfirmsOrDie方法如果返回false则会关闭channel,则接下来无法发送消息到broker
public class MsgConfirmCallback implements RabbitTemplate.ConfirmCallback {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(ack){
// 成功
}else{
// 失败
}
}
}
六、分布式可靠性保障
首先说到可靠性,rabbitmq的可靠性分为两部分,一是要保障可靠生产,二是保障可靠消费。二者实现方式不同。
简单来说可靠生产可以用冗余数据的存储+定时任务实时轮询查取进行处理,也就是将投递给MQ的数据在发送的同时存库,然后开启消息投递成功确认机制,这样如果投递成功,MQ返回回调,然后更新冗余库中的数据状态为发送成功。如果没有投递成功,那么定时任务查询,将未发送成功的数据进行重试,如果重试次数超过阈值,可以进行人工排错处理。
简单来说可靠消费可以用手动ack+try catch拒绝策略+死信队列进行处理(此处注意,如果使用try catch拒绝策略中重发设置为true,那么将造成MQ重发死循环)
如下所示:
@RabbitListener(queues = {"queue"})
public void consumer(String msg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
try {
// 获取消息
System.out.println("消息:"+ msg);
1 / 0; //故意设置出现异常
// 手动ack
channel.basicAck(tag, false);
} catch (Exception ex) {
// 出现异常后,拒绝,此时消息队列会将数据丢掉,如果业务可以允许丢掉,那无所谓
// 但是如果不允许,那么添加死信队列,将数据投递到死信队列中
// 参数1为消息的tag 参数2为是否多条处理 参数3为是否重发
// 如果在try catch中设置为重发,那么会造成死循环,所以一般不要这么设置
// 如果需要失败重试,并且专门配置了失败重试次数,那么就不要用try catch,二者不可兼容,切记!!!
channel.basicNack(tag, false, false);
}
}
七、需要注意的小细节
1.如果队列已存在,如果直接不删除,就代码动态更改队列参数,会报错,需要修改队列参数的时候,请先删除再重新创建
2.如何所有连接均处于blocking状态,所有服务均被挂起,一般都是磁盘或者内存满了