文章目录
引入RabbitMQ
依赖
<!--mq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
常用配置
application.yml
# rabbitmq配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.password=guest
spring.rabbitmq.username=guest
spring.rabbitmq.virtual-host=MyHost1 //自定义的,在RabbitMQ页面手动创建一个,不配置使用RabbitMQ默认的"/" host
##消息确认配置项(手动确认要配置)
#1.确认消息已发送到交换机(Exchange)
spring.rabbitmq.publisher-confirm-type=correlated
#2.确认消息已经发送到队列(Queue)
spring.rabbitmq.publisher-returns=true
虚拟主机
rabbitMQ默认的虚拟主机为: “/” 。可通过RabbitMQ管理界面创建自定义虚拟主机,也可以通过以下命令:
#创建虚拟主机
rabbitmqctl add vhost [vhost_name]
#删除虚拟主机
rabbitmqctl delete vhost [vhost_name]
#列出虚拟主机
rabbitmqctl list_vhosts
消息投递策略
重启RabbitMQ服务器后队列和交换机会消失(durable属性默认为false),可设置队列和交换机的durable属性为true来持久化。
常用的交换机类型
direct
消息就投递到对应路由键匹配的队列
fanout
投递消息给所有绑定在当前交换机上面的队列
topic:
*,如:topic.hello
只有是消息携带的路由键是topic.hello,才会分发到该队列。
#,如:topic.#
只要是消息携带的路由键是以topic.开头,都会分发到该队列
常用
手动确认用法,简单用法demo参考:mycourse,example1项目。
消息确认
推送消息存在四种情况:
①消息推送到server,但是在server里找不到交换机
②消息推送到server,找到交换机了,但是没找到队列
③消息推送到sever,交换机和队列啥都没找到
④消息推送成功
消费者端
@Override
public void onMessage(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
// todo:业务处理
//业务处理结果成功
if (true) {
// 消费确认
//channel.basicAck(deliveryTag, false) 第二个参数,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息
// channel.basicReject(deliveryTag, true);//第二个参数,true会重新放回队列,所以需要自己根据业务逻辑判断什么时候使用拒绝
channel.basicAck(deliveryTag, false);
} else {
//
// channel.basicNack(deliveryTag, false, true);
// 第一个参数依然是当前消息到的数据的唯一id;
// 第二个参数是指是否针对多条消息;如果是true,也就是说一次性针对当前通道的消息的tagID小于当前这条消息的,都拒绝确认。
// 第三个参数是指是否重新入列,也就是指不确认的消息是否重新丢回到队列里面去。
channel.basicNack(deliveryTag, false, false);
}
}
自动确认
自动确认, 这也是默认的消息确认情况。 AcknowledgeMode.NONE
根据情况确认
AcknowledgeMode.AUTO:
手动确认
开启AcknowledgeMode.MANUAL。
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
basic.ack用于肯定确认
basic.nack用于否定确认(这是AMQP 0-9-1的RabbitMQ扩展)
basic.reject用于否定确认,但与basic.nack相比有一个限制:一次只能拒绝单条消息
消费者端以上的3个方法都表示消息已经被正确投递
,但是basic.ack表示消息已经被正确处理
。而basic.nack,basic.reject表示没有被正确处理
。
1. channel.basicReject(deliveryTag, true);
拒绝消费当前消息,如果第二参数传入true,就是将数据重新丢回队列里,那么下次还会消费这消息。设置false,就是告诉服务器,我已经知道这条
消息数据了,因为一些原因拒绝它,而且服务器也把这个消息丢掉就行。 下次不想再消费这条消息了。
使用拒绝后重新入列这个确认模式要谨慎,因为一般都是出现异常的时候,catch异常再拒绝入列,选择是否重入列,如果使用不当会导致一些每次都
被你重入列的消息一直消费-入列-消费-入列这样循环,会导致消息积压。
2.channel.basicNack(deliveryTag, false, true);
第一个参数依然是当前消息到的数据的唯一id;
第二个参数是指是否针对多条消息;如果是true,也就是说一次性针对当前通道的消息的tagID小于当前这条消息的,都拒绝确认。
第三个参数是指是否重新入列,也就是指不确认的消息是否重新丢回到队列里面去。
生成者端
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
//如果confirm返回成功 则进行更新消息投递成功
String msgId = correlationData.getId();
// 更新msgId对应状态为deliver_success
} else {
//失败则进行具体的后续操作:重试 等
LOG.warn("<--消息发送到Exchange失败-->" + correlationData + cause);
}
}
})
交换机或路由键不存在
指定的路由键不存在,或者交换机不存在,那么消息就会return。
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
LOG.warn("<---消息从Exchange路由到Queue失败--->");
LOG.warn("ReturnCallback: "+"消息:"+message);
LOG.warn("ReturnCallback: "+"回应码:"+replyCode);
LOG.warn("ReturnCallback: "+"回应信息:"+replyText);
LOG.warn("ReturnCallback: "+"交换机:"+exchange);
LOG.warn("ReturnCallback: "+"路由键:"+routingKey);
}
});
消费端限流
Qos在非自动确认消息的情况下,在一定数量的消息未被消费前,不进行消费新的消息。
// prefetchSize消息的限制大小,一般设置为0,在生产端限制
// prefetchCount 我们一次最多消费多少条消息,一般设置为1
// global,一般设置为false,在消费端进行限制
channel.basicQos(int prefetchSize, int prefetchCount, boolean global)
// 使用
channel.basicQos(0, 1, false);
channel.basicConsume(queueName, false, new MyConsumer(channel));
死信队列(DLX)
进入死信队列3种情况:
1、basic.reject/basic.nack 并且 requeue为false(不重回队列)
2、消息TTL过期
3、队列达到最大的长度