(rabbitmq学习内容来源于蚂蚁课堂)
死信队列 听上去像 消息“死”了 ,其实也有点这个意思,
死信队列 是 当消息在一个队列 因为下列原因:
1.消息被拒绝(basic.reject或basic.nack)并且requeue=false.
2.消息TTL过期
3.队列达到最大长度(队列满了,无法再添加数据到mq中)
应用场景分析
在定义业务队列的时候,可以考虑指定一个死信交换机,并绑定一个死信队列,当消息变成死信时,该消息就会被发送到该死信队列上,这样就方便我们查看消息失败的原因了。
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false); 丢弃消息
本文基于上一篇文章
1.生产者服务,定义死信队列,生产者代码无边化。
2.消费者服务,rabbitmq配置,改为手动应答
acknowledge-mode: manual
3.编写死信队列消费者代码
package com.google.msg.sms;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author wk
* @Description:死信队列消费者
* @date 2019/12/12 17:03
**/
@Component
public class FanoutSmsDeadConsumer {
//模拟messageId存储 消息是否被消费存储
static Map<String, Boolean> messageIdsCache = new HashMap<>();
@RabbitListener(queues = "dead_queue")
public void process(Message message, @Headers Map<String, Object> headers, Channel channel) throws Exception {
String messageId = message.getMessageProperties().getMessageId();
if (!messageIdsCache.containsKey(messageId)) {
messageIdsCache.put(messageId, false);
}
if (messageIdsCache.get(messageId)) {
System.out.println("死信队列FanoutSmsDeadConsumer 该消息已被消费!");
return;
}
String msg = new String(message.getBody(), "UTF-8");
//消息是否消费
messageIdsCache.put(messageId, true);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("短信服务调用成功!死信队列FanoutSmsDeadConsumer " + msg + ",messageId: " + messageIdsCache.toString());
}
}
4.编写短信消费者代码
package com.google.msg.sms;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author wk
* @Description:RabbitMQ手动应答模式;普通队列绑定死信队列
* @date 2019/12/12 17:03
**/
@Component
public class FanoutSmsConsumer4 {
//模拟messageId存储 消息是否被消费
static Map<String, Boolean> messageIdsCache = new HashMap<>();
@RabbitListener(queues = "FANOUT_SMS_QUEUE4")
public void process(Message message, @Headers Map<String, Object> headers, Channel channel) throws Exception {
String messageId = message.getMessageProperties().getMessageId();
if (!messageIdsCache.containsKey(messageId)) {
messageIdsCache.put(messageId, false);
}
if (messageIdsCache.get(messageId)) {
System.out.println("FanoutSmsConsumer4 该消息已被消费!");
return;
}
String msg = new String(message.getBody(), "UTF-8");
try {
JSONObject jsonObject = JSONObject.parseObject(msg);
int result = 1 / jsonObject.getInteger("number");
System.out.println("result:" + result);
//业务执行成功,消息被消费
messageIdsCache.put(messageId, true);
//手动ack
Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
//手动签收
channel.basicAck(deliveryTag, false);
System.out.println("短信服务调用成功!FanoutSmsConsumer4 " + msg+",messageId: "+messageIdsCache.toString());
} catch (Exception e) {
e.printStackTrace();
//拒绝消费消息(丢弃消息)给死信队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
}
}
}
5.测试运行:
①消费者4 FanoutSmsConsumer4 正常消费情况:
启动生产者服务,投递消息 值i=1
启动消费者服务,
控制台输出:
result:1
短信服务调用成功!FanoutSmsConsumer4 {"number":1,"sms":"1357760345@qq.com","content":"SpringBoot整合RabbitMQ","timestamp":1582909129349},messageId: {0e7dda37-a900-48bf-adc2-999d82961740=true}
②消费者4 FanoutSmsConsumer4 消费失败情况:投递消息 值i=0
http://127.0.0.1:2100/sendMessage?queueName=FANOUT_SMS_QUEUE4&i=0
消费者控制台报错
java.lang.ArithmeticException: / by zero
at com.google.msg.sms.FanoutSmsConsumer4.process(FanoutSmsConsumer4.java:39)
消息被死信队列消费者消费:
控制台输出:
短信服务调用成功!死信队列FanoutSmsDeadConsumer {"number":0,"sms":"1357760345@qq.com","content":"SpringBoot整合RabbitMQ","timestamp":1582909402480},messageId: {2abcb252-9321-4bca-bba5-b64448f43b25=true}