手动应答模式(manual)
解释:
手动应答:既是当消费者消费了队列中消息时需要给队列一个应答,告诉队列这条消息我已经消费了,可以删除了;
若是不应答,即使消费了 队列没收到消费成功的提示 所有消息会一直在队列中;
注意 注意 注意:重要的事情说三遍,下面说的很重要
场景:当我们使用了手动应答模式,消费者若是成功消费了信息,我们给队列一个成功应答(channel.basicAck(deliveryTag,false);),然后队列收到应答后就会把此消息删除,这点时毋庸置疑的,因为我们已成功消费了这个消息,也不想让此消息继续留在队列中;
但是,若是消费者消费消息失败了,该怎么办? 这时不能再给队列一个成功的应答(给了那这条消息不就丢失了吗),不给应答呢 消息又一直在队列中,而倘若我们给一个拒绝应答(channel.basicReject(deliveryTag,true) true会重回队列,若是false 消息就丢了,一般都会设置为true;),那么你就成功掉进坑里了,这样队列会一直循环投递消息,而消费者这边又不能成功消费,消费者又拒绝应答,队列又投递消息......
对,此时就会进入死循环,搞不好光错误日志就会沾满内存;这时该有人会说了,不对,我们在配置文件中不是开启了重试,并且配置了最大重试次数了吗?
如下配置:
* #是否开启自动重试 默认为false 不开启
* spring.rabbitmq.listener.simple.retry.enabled=true
* #最大重试次数
* spring.rabbitmq.listener.simple.retry.max-attempts=5
* #最大重试时间间隔
* spring.rabbitmq.listener.simple.retry.max-interval=20000ms
* #重试时间间隔
* spring.rabbitmq.listener.simple.retry.initial-interval=2000ms
* # 最大重试间隔*乘数
* #应用于上一重试间隔的乘数 第一次(重试时间间隔)2s 4s 8s 16s 32s 此处32s>20s 以后都以20s为间隔 总的次数为最大重试次数
* spring.rabbitmq.listener.simple.retry.multiplier=2
那么,应该会在最大重试次数试完还不成功就应该不尝试投递了啊,这样不应该走死循环了啊!
其实这就是要特别注意的点,当我们开启了手动应答,上面那些配置其实都已经失效了!!! 所以就会进入死循环!
但是在开发中我们不想让消息丢失,那么开启自动应答显然不合理,而开启手动应答时当出现错误时(没成功消费)又会进入死循环,那该怎么解决呢?
解决:引入死信队列; 当消息没被成功消费时 我们把这条消息投递到私信队列中,然后我们再人为的干预处理此消息即可!
自动应答:顾名思义 当消费者成功消费了队列中消息,队列就会自动的把此消息从队列中删除,若是没有消费者或者消费者消费失败,队列在尝试最大重试次数后就会把此消息删除;
/**
* 消费者
*/
@Component
@Slf4j
public class DirectConsumer {
/**
* 手动应答模式(manual)
* 解释:
* 手动应答:既是当消费者消费了队列中消息时需要给队列一个应答,告诉队列这条消息我已经消费了,可以删除了;
* 若是不应答,即使消费了 队列没收到消费成功的提示 所有消息会一直在队列中;
* 注意 注意 注意:重要的事情说三遍,下面说的很重要
* 场景:当我们使用了手动应答模式,消费者若是成功消费了信息,我们给队列一个成功应答(channel.basicAck(deliveryTag,false);),
* 然后队列收到应答后就会把此消息删除,这点时毋庸置疑的,因为我们已成功消费了这个消息,也不想让此消息继续留在队列中;
* 但是,若是消费者消费消息失败了,该怎么办? 这时不能再给队列一个成功的应答(给了那这条消息不就丢失了吗),不给应答呢 消息又一直在队列中
* 而倘若我们给一个拒绝应答(channel.basicReject(deliveryTag,true) true会重回队列,若是false 消息就丢了,一般都会设置为true;)
* 那么你就成功掉进坑里了,这样队列会一直循环投递消息,而消费者这边又不能成功消费,消费者又拒绝应答,队列又投递消息......
* 对,此时就会进入死循环,搞不好光错误日志就会沾满内存;这时该有人会说了,不对,我们在配置文件中不是开启了重试,并且配置了最大重试次数了吗?
* 如下配置:
* #是否开启自动重试 默认为false 不开启
* spring.rabbitmq.listener.simple.retry.enabled=true
* #最大重试次数
* spring.rabbitmq.listener.simple.retry.max-attempts=5
* #最大重试时间间隔
* spring.rabbitmq.listener.simple.retry.max-interval=20000ms
* #重试时间间隔
* spring.rabbitmq.listener.simple.retry.initial-interval=2000ms
* # 最大重试间隔*乘数
* #应用于上一重试间隔的乘数 第一次(重试时间间隔)2s 4s 8s 16s 32s 此处32s>20s 以后都以20s为间隔 总的次数为最大重试次数
* spring.rabbitmq.listener.simple.retry.multiplier=2
* 那么,应该会在最大重试次数试完还不成功就应该不尝试投递了啊,这样不应该走死循环了啊!
* 其实这就是要特别注意的点,当我们开启了手动应答,上面那些配置其实都已经失效了!!! 所以就会进入死循环!
* 但是在开发中我们不想让消息丢失,那么开启自动应答显然不合理,而开启手动应答时当出现错误时(没成功消费)又会进入死循环,那该怎么解决呢?
*
* 解决: 引入死信队列; 当消息没被成功消费时 我们把这条消息投递到私信队列中,然后我们再人为的干预处理此消息即可!
*
*
* 自动应答:顾名思义 当消费者成功消费了队列中消息,队列就会自动的把此消息从队列中删除,若是没有消费者或者消费者
* 消费失败,队列在尝试最大重试次数后就会把此消息删除;
*
* 注解含义:
* 1、@RabbitHandler handler真正的执行者
* 2、@RabbitListener 监听DirectQueue-01这个队列
*
* @param user 接受的消息类型为user(生产者发送的为user类型)
* @param message
* @param channel
* @throws IOException
*/
@RabbitHandler
@RabbitListener(queues = RabbitConfig.QUEUE_KEY_03)
public void process2(User user, Message message, Channel channel) throws IOException {
// long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//业务开始
if (user.getId().equals(5)) {
int a=1/0;
}
System.out.println("接受到消息,并正常处理结束"+ JSONUtil.toJsonStr(user));
//业务结束
/**
* 确认应答
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:当前消息在队列中的的索引;
* multiple:为true的话就是批量确认 是消费一个就应答还是一批处理完再应答;通常都是false 一个一个应答
*/
// channel.basicAck(deliveryTag,false);
}catch (Exception ex){
System.out.println(ex.getMessage());
System.out.println("接受到消息,发生异常"+ JSONUtil.toJsonStr(user));
System.out.println(user);
throw ex;
//拒绝 true的时候拒绝,false时消息就丢了
// channel.basicReject(deliveryTag,true);
}
}
}