基础
https://blog.csdn.net/qq_41520636/article/details/115473826
进阶
https://blog.csdn.net/qq_41520636/article/details/115562164
消息可靠性投递
pom引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
生产端的application.yml设置
spring:
rabbitmq:
# 消息可靠性投递
publisher-returns: true
# 消息可靠性投递
publisher-confirms: true
# 交换机处理消息失败的模式
template:
mandatory: true
配置类
package com.hikktn.config;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName RabbitMQConfig
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 14:49
* @Version 1.0
*/
@Configuration
public class RabbitMQConfig {
@Autowired
private CachingConnectionFactory connectionFactory;
// 路由模式
public static final String DIRECT_QUEUE_NAME_1 = "routing_direct_queue_1";
public static final String DIRECT_QUEUE_NAME_2 = "routing_direct_queue_2";
public static final String TEST_DIRECT_EXCHANGE = "direct_exchange";
private static final String DIRECT_ROUTING_KEY_INSERT = "insert";
private static final String DIRECT_ROUTING_KEY_UPDATE = "update";
@Bean
public RabbitTemplate rabbitTemplate(){
// 使用confirm-callback ,必须配置publisherConfirms 为true
connectionFactory.setPublisherConfirms(true);
// 使用return-callback,必须配置publisherReturns为true
connectionFactory.setPublisherReturns(true);
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 使用return-callback时必须设置mandatory为true,设置交换机处理失败消息的模式
rabbitTemplate.setMandatory(true);
// 确认模式
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("消息唯一标识:" + correlationData);
System.out.println("确认结果:" + ack);
System.out.println("失败原因:" + cause);
if (!ack) {
System.err.println("异常处理...");
}
}
});
// 回退模式: 当消息发送给Exchange后,Exchange路由到Queue失败是 才会执行 ReturnCallBack
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.err.println("消息丢失:return exchange: " + exchange + ", routingKey: " + routingKey + ", replyCode" +
": " + replyCode + ", replyText: " + replyText);
}
});
return rabbitTemplate;
}
// 路由模式
@Bean("routing_direct_queue_1")
public Queue createRoutingQueue1() {
return new Queue(DIRECT_QUEUE_NAME_1, true, false, false, null);
}
@Bean("routing_direct_queue_2")
public Queue createRoutingQueue2() {
return new Queue(DIRECT_QUEUE_NAME_2, true, false, false, null);
}
// 队列与交换机进行绑定
@Bean
public Binding bindingQueueAndDirectExchange1(@Qualifier("routing_direct_queue_1") Queue queue, @Qualifier("direct_exchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(DIRECT_ROUTING_KEY_INSERT);
}
@Bean
public Binding bindingQueueAndDirectExchange2(@Qualifier("routing_direct_queue_2") Queue queue, @Qualifier("direct_exchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(DIRECT_ROUTING_KEY_UPDATE);
}
}
生产者
package com.hikktn.producer;
import com.hikktn.config.RabbitMQConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @ClassName Sender
* @Description 生产者
* @Author lisonglin
* @Date 2021/4/9 16:54
* @Version 1.0
*/
@Component
public class Sender {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 生产者1 (路由模式)
*
* @param routingKey
* @param message
*/
public void sendDirectTest1(String routingKey, String message) {
CorrelationData correlationData = new CorrelationData("1234567890"); // id + 时间戳 全局唯一
rabbitTemplate.convertAndSend(RabbitMQConfig.TEST_DIRECT_EXCHANGE, routingKey, message, correlationData);
}
/**
* 生产者2 (路由模式)
*
* @param routingKey
* @param msg
*/
public void sendDirectTest2(String routingKey, String msg) {
CorrelationData correlationData = new CorrelationData("0123456789"); // id + 时间戳 全局唯一
rabbitTemplate.convertAndSend(RabbitMQConfig.TEST_DIRECT_EXCHANGE, routingKey, msg, correlationData);
}
}
消费者1
package com.hikktn.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName DirectListener1
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 21:55
* @Version 1.0
*/
@Component
public class DirectListener1 {
@RabbitListener(queues = "routing_direct_queue_1")
public void receive(String message){
System.out.println("消费者1接收到的消息为:" + message);
}
}
消费者2
package com.hikktn.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName DirectListener1
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 21:55
* @Version 1.0
*/
@Component
public class DirectListener2 {
@RabbitListener(queues = "routing_direct_queue_2")
public void receive(String message){
System.out.println("消费者1接收到的消息为:" + message);
}
}
控制器
package com.hikktn.controller;
import com.hikktn.producer.Sender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName ConsumerController
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 19:27
* @Version 1.0
*/
@RestController
public class ConsumerController {
@Autowired
private Sender sender;
@GetMapping("/getDirect")
public String sendDirect(){
sender.sendDirectTest1("insert","direct模式1,发送了消息");
sender.sendDirectTest2("update","direct模式2,发送了消息");
return "ok";
}
}
测试confirm ----- 交换机名改为没有创建的交换机,造成找不到交换机的错误。
/**
* 生产者1 (路由模式)
*
* @param routingKey
* @param message
*/
public void sendDirectTest1(String routingKey, String message) {
MessageProperties messageProperties = new MessageProperties();
//在生产环境中这里不用Message,而是使用 fastJson 等工具将对象转换为 json 格式发送
Message msg = new Message(message.getBytes(), messageProperties);
CorrelationData correlationData = new CorrelationData("1234567890"); // id + 时间戳 全局唯一
rabbitTemplate.convertAndSend("direct_exchange123", routingKey, msg, correlationData);
}
测试 returned ---- 路由key值改为没有创建的routingKey,造成找不到routingKey的错误。
/**
* 生产者1 (路由模式)
*
* @param routingKey
* @param message
*/
public void sendDirectTest1(String routingKey, String message) {
CorrelationData correlationData = new CorrelationData("1234567890"); // id + 时间戳 全局唯一
rabbitTemplate.convertAndSend(RabbitMQConfig.TEST_DIRECT_EXCHANGE, "update123", message, correlationData);
}
确认模式和回退模式区别
确认模式:监听交换机Exchange是否正常收到消息,正常收到消息,可以进行回调函数,后续业务处理;异常情况,同样也能够进行处理。
回退模式:监听队列Queue是否正常收到消息,正常收到消息,默认不处理;异常收到消息,拦截请求,进行重新发送队列。
Consumer ACK
消费者application.yml
spring:
rabbitmq:
listener:
simple:
# 消息传递(ack是否手动确认,none:不确认,auto:自动确认,manual:手动确认)
acknowledge-mode: manual
手动确认前和手动确认后
改造一下消费者1和消费者2
消费者1
package com.hikktn.listener;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @ClassName DirectListener1
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 21:55
* @Version 1.0
*/
@Component
public class DirectListener1 {
@RabbitListener(queues = "routing_direct_queue_1")
public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("direct消费者1接收到的消息为:" + message);
try {
/**
* 无异常就确认消息
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:取出来当前消息在队列中的的索引;
* multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
* deliveryTag为5及其以下的消息;一般设置为false
*/
channel.basicAck(tag, false);
}catch (Exception e){
/**
* 有异常就绝收消息
* basicNack(long deliveryTag, boolean multiple, boolean requeue)
* requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
* false:将消息丢弃
*/
channel.basicNack(tag,false,true);
}
}
}
消费者2
package com.hikktn.listener;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName DirectListener1
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 21:55
* @Version 1.0
*/
@Component
public class DirectListener2 {
@RabbitListener(queues = "routing_direct_queue_2")
public void receive(String message){
System.out.println("direct消费者2接收到的消息为:" + message);
}
}
测试
可以非常清楚地看到两个队列里的消息,有一条消息没有确认,还在运行中....
当选择手动确认后,必须加上channel.basicAck(tag, false);这句代码来确认结果正常终了,否则每次重启Spring服务,该消息又会重复发送。
而channel.basicNack(tag,false,true);则会重复发送消息,直到确认消息为止。
测试
package com.hikktn.listener;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @ClassName DirectListener1
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 21:55
* @Version 1.0
*/
@Component
public class DirectListener2 {
@RabbitListener(queues = "routing_direct_queue_2")
public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("direct消费者2接收到的消息为:" + message);
// 手动否认
channel.basicNack(tag,false,true);
}
}
结果,消息一直在发送
消费端限流和并发消费
spring:
rabbitmq:
listener:
simple:
# 消息传递(ack是否手动确认,none:不确认,auto:自动确认,manual:手动确认)
acknowledge-mode: manual
# 消费端限流(处理消息的限制)
prefetch: 1
# 接收的消费队列数(并发)
concurrency: 5
测试
@RabbitListener(queues = "simple_queue")
public void receive(Message message, Channel channel) {
String messageRec = new String(message.getBody());
System.out.println("simple模式接收到了消息:"+messageRec);
// try {
// channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
// } catch (IOException e) {
// System.out.println("报错了------------------"+e.getMessage());
// }
}
@GetMapping("/getSimple")
public String sendSimple(){
for (int i = 0; i < 10; i++) {
sender.sendSimpleTest("simple模式,发送了消息");
}
return "ok";
}
结果
一次收到五条消息,但是消费的消息条数,其实是一条一条处理的,只是开启了并发。所以才一次性接收5条消息。
TTL
设置参数
x-message-ttl:单位:ms(毫秒),会对整个队列消息统一过期。
expiration:单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断这一消息是否过期。
如果这两个都设置了,以时间短的为准。
第一种方式
配置类
@Bean("topic_queue_2")
public Queue createTopicQueue2() {
Map<String, Object> args = new HashMap<>();
// x-message-ttl指队列的过期时间
// 队列中的消息未被消费则30秒后过期 TTL(指定队列的消息过期时间)
args.put("x-message-ttl", 30000);
return QueueBuilder.durable(TOPIC_QUEUE_NAME_2).withArguments(args).build();
}
取消消费者类
// @RabbitListener(queues = "topic_queue_1")
// @RabbitHandler
// public void receive1(Message message, Channel channel, String msg) throws IOException {
// long deliveryTag = message.getMessageProperties().getDeliveryTag();
// System.out.println("消费者1接收到的消息为:" + msg);
// String messageRec = new String(message.getBody());
// System.out.println("消费者1接收到的消息为:" + messageRec);
// try {
// // 手动签收
// channel.basicAck(deliveryTag, true);
// } catch (IOException e) {
// System.out.println("报错了------------------"+e.getMessage());
// // 拒绝签收,不重回队列 requeue=false
// channel.basicNack(deliveryTag,true,false);
// }
// }
// @RabbitListener(queues = "topic_queue_2")
// public void receive2(Message message, Channel channel) {
// String messageRec = new String(message.getBody());
// System.out.println("消费者2接收到的消息为:" + messageRec);
// try {
// channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
// } catch (IOException e) {
// System.out.println("报错了------------------" + e.getMessage());
// }
// }
取消回调方法和回退方法
// @Bean
// public RabbitTemplate rabbitTemplate(){
// //若使用confirm-callback ,必须要配置publisherConfirms 为true
// connectionFactory.setPublisherConfirms(true);
// //若使用return-callback,必须要配置publisherReturns为true
// connectionFactory.setPublisherReturns(true);
// RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// //使用return-callback时必须设置mandatory为true,或者在配置中设置mandatory-expression的值为true
// rabbitTemplate.setMandatory(true);
// // 如果消息没有到exchange,则confirm回调,ack=false; 如果消息到达exchange,则confirm回调,ack=true
// rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
// @Override
// public void confirm(CorrelationData correlationData, boolean ack, String cause) {
// System.out.println("消息唯一标识:" + correlationData);
// System.out.println("确认结果:" + ack);
// System.out.println("失败原因:" + cause);
// if (!ack) {
// System.err.println("异常处理...");
// }
// }
// });
//
// //如果exchange到queue成功,则不回调return;如果exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
// rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
// @Override
// public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
// System.err.println("消息丢失:return exchange: " + exchange + ", routingKey: " + routingKey + ", replyCode" +
// ": " + replyCode + ", replyText: " + replyText);
// }
// });
//
// return rabbitTemplate;
// }
取消发生队列1 的消息
@GetMapping("/getTopic")
public String sendTopic(){
// sender.sendTopicTest1("1213457107.@qq.com","topic模式1,发送给1213457107@qq.com消息");
sender.sendTopicTest2("sms.@qq.com","topic模式2,发送给sms.@qq.com消息");
return "ok";
}
发送消息
消息处于没有接收状态
未没有发送消息前的状态
发送后,topic_queue_2队列30秒钟后过期
30秒钟过期
问题
但是有一问题,明明我并没有发送topic_queue_1消息,可是topic_queue_1队列自从发送了消息后,就一直处于ready状态,这是我不明白的点。
第二种方式
/**
* 生产者1 (通配符模式)
*
* @param routingKey
* @param msg
*/
public void sendTopicTest1(String routingKey, String msg) {
MessageProperties messageProperties = new MessageProperties();
// 设置过期时间,单位:毫秒 TTL (所有发送过来的队列消息过期时间)
messageProperties.setExpiration("20000");
byte[] msgBytes = msg.getBytes();
Message message = new Message(msgBytes, messageProperties);
rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE_NAME, routingKey, message);
}
测试
取消发送队列2的消息,放开队列1的消息
@GetMapping("/getTopic")
public String sendTopic(){
sender.sendTopicTest1("1213457107.@qq.com","topic模式1,发送给1213457107@qq.com消息");
// sender.sendTopicTest2("sms.@qq.com","topic模式2,发送给sms.@qq.com消息");
return "ok";
}
发送消息
消息没有被消费
测试前
测试后
20秒钟后过期
死信队列
配置类
注意:死信队列必须优先创建,否则会报找不到交换机的错误。
package com.hikktn.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName RabbitMQConfig
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 14:49
* @Version 1.0
*/
@Configuration
public class RabbitMQConfig {
// 路由模式 - 死信队列
public static final String DEAD_EXCHANGE_NAME = "dead_exchange";
public static final String LIVE_EXCHANGE_NAME = "live_exchange";
private static final String DEAD_QUEUE_NAME_1 = "dead_queue_1";
private static final String LIVE_QUEUE_NAME_2 = "live_queue_2";
private static final String TOPIC_ROUTING_KEY_DEAD = "Dead";
private static final String TOPIC_ROUTING_KEY_LIVE = "Live";
// 死信队列
// 死队列
@Bean("dead_queue_1")
public Queue createDeadDlxQueue1() {
Map<String,Object> args = new HashMap<>(16);
return QueueBuilder.durable(DEAD_QUEUE_NAME_1).withArguments(args).build();
}
// 死交换机
@Bean("dead_exchange")
public Exchange createDeadExchange() {
return ExchangeBuilder.directExchange(DEAD_EXCHANGE_NAME).durable(true).build();
}
// 绑定死队列和死交换机
@Bean
public Binding bindingDeadQueueAndDlxExchange(@Qualifier("dead_queue_1") Queue queue,
@Qualifier("dead_exchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY_DEAD).noargs();
}
// 活队列
@Bean("live_queue_2")
public Queue createLiveDlxQueue2() {
// 死信队列
Map<String,Object> args = new HashMap<>(16);
// x-message-ttl:设置队列的过期时间
args.put("x-message-ttl",20000);
// x-max-length:设置队列的长度限制
args.put("x-max-length",20);
// x-dead-letter-exchange:死信交换机名称
args.put("x-dead-letter-exchange",DEAD_EXCHANGE_NAME);
// x-dead-letter-routing-key:发送给死信交换机的routingkey
args.put("x-dead-letter-routing-key", ROUTING_KEY_DEAD);
return QueueBuilder.durable(LIVE_QUEUE_NAME_2).withArguments(args).build();
}
// 活交换机
@Bean("live_exchange")
public Exchange createLiveExchange() {
return ExchangeBuilder.directExchange(LIVE_EXCHANGE_NAME).durable(true).build();
}
// 绑定活队列和活交换机
@Bean
public Binding bindingLiveQueueAndDlxExchange(@Qualifier("live_queue_2") Queue queue,
@Qualifier("live_exchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY_LIVE).noargs();
}
}
第一种方式:出现异常
消费者1
package com.hikktn.listener;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @ClassName DirectListener1
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 21:55
* @Version 1.0
*/
@Component
public class LiveListener {
@RabbitListener(queues = "live_queue_2")
public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("live_queue_2消费者1接收到的消息为:" + message);
try {
int i = 3 / 0;
/**
* 无异常就确认消息
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:取出来当前消息在队列中的的索引;
* multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
* deliveryTag为5及其以下的消息;一般设置为false
*/
channel.basicAck(tag, false);
}catch (Exception e){
/**
* 有异常就绝收消息
* basicNack(long deliveryTag, boolean multiple, boolean requeue)
* requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
* false:将消息丢弃
*/
channel.basicNack(tag,false,false);
}
}
}
消费者2
package com.hikktn.listener;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @ClassName DirectListener1
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 21:55
* @Version 1.0
*/
@Component
public class DeadListener {
@RabbitListener(queues = "dead_queue_1")
public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("dead_queue_1消费者1接收到的消息为:" + message);
try {
int i = 3 / 1;
/**
* 无异常就确认消息
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:取出来当前消息在队列中的的索引;
* multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
* deliveryTag为5及其以下的消息;一般设置为false
*/
channel.basicAck(tag, false);
}catch (Exception e){
/**
* 有异常就绝收消息
* basicNack(long deliveryTag, boolean multiple, boolean requeue)
* requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
* false:将消息丢弃
*/
channel.basicNack(tag,true,false);
}
}
}
生产者
package com.hikktn.producer;
import com.hikktn.config.RabbitMQConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @ClassName Sender
* @Description 生产者
* @Author lisonglin
* @Date 2021/4/9 16:54
* @Version 1.0
*/
@Component
public class Sender {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 生产者2 (通配符模式)
*
* @param routingKey
* @param msg
*/
public void sendDeadTopicTest(String routingKey, String msg) {
rabbitTemplate.convertAndSend(RabbitMQConfig.LIVE_EXCHANGE_NAME, routingKey, msg);
}
}
控制器
@GetMapping("/getDead")
public String sendDeadTopic(){
// 发送的消息,必须key和exchange和活信一样
sender.sendDeadTopicTest("Live","死信队列测试,发送给Dead消息");
return "ok";
}
结果
第二种方式:队列超时
测试
取消各种消费者
// @RabbitListener(queues = "live_queue_2")
public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("live_queue_2消费者1接收到的消息为:" + message);
try {
// int i = 3 / 0;
/**
* 无异常就确认消息
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:取出来当前消息在队列中的的索引;
* multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
* deliveryTag为5及其以下的消息;一般设置为false
*/
channel.basicAck(tag, false);
}catch (Exception e){
/**
* 有异常就绝收消息
* basicNack(long deliveryTag, boolean multiple, boolean requeue)
* requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
* false:将消息丢弃
*/
channel.basicNack(tag,false,false);
}
}
// @RabbitListener(queues = "dead_queue_1")
public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("dead_queue_1消费者1接收到的消息为:" + message);
try {
// int i = 3 / 1;
/**
* 无异常就确认消息
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:取出来当前消息在队列中的的索引;
* multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
* deliveryTag为5及其以下的消息;一般设置为false
*/
channel.basicAck(tag, false);
}catch (Exception e){
/**
* 有异常就绝收消息
* basicNack(long deliveryTag, boolean multiple, boolean requeue)
* requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
* false:将消息丢弃
*/
channel.basicNack(tag,true,false);
}
}
测试前
结果
20秒钟后
第三种方式:队列超过限制长度
测试
/**
* 生产者2 (通配符模式)
*
* @param routingKey
* @param msg
*/
public void sendDeadTopicTest(String routingKey, String msg) {
for (int i = 0; i < 30; i++) {
rabbitTemplate.convertAndSend(RabbitMQConfig.LIVE_EXCHANGE_NAME, routingKey, msg);
}
}
超过队列的限制
20秒钟后过期
延迟队列
就是利用DLX+TTL技术实现,上面的死信队列,只需要注释掉活队列的消费者,而后利用活队列的过期时间,自动将活队列的消息变为死队列。
具体看代码
package com.hikktn.listener;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @ClassName DirectListener1
* @Description TODO
* @Author lisonglin
* @Date 2021/4/9 21:55
* @Version 1.0
*/
@Component
public class LiveListener {
// @RabbitListener(queues = "live_queue_2")
public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("live_queue_2消费者1接收到的消息为:" + message);
try {
// int i = 3 / 0;
/**
* 无异常就确认消息
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:取出来当前消息在队列中的的索引;
* multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
* deliveryTag为5及其以下的消息;一般设置为false
*/
channel.basicAck(tag, false);
}catch (Exception e){
/**
* 有异常就绝收消息
* basicNack(long deliveryTag, boolean multiple, boolean requeue)
* requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
* false:将消息丢弃
*/
channel.basicNack(tag,false,false);
}
}
}
测试效果 如下:
以上,完结!