![](https://img.kancloud.cn/93/4f/934f37b6c1e8f1197b5d936713c564ec_606x234.png)
> **1.消费者从队列中获取到消息后,会直接确认签收,假设消费者宕机或者程序出现异常,数据没有正常消费,这种情况就会出现数据丢失**。
> 2. 消费模式有两种:rabbitmq推给消费者(订阅)、消费者主动拉取
> 3. 除非对消息可靠性要求比较的场景,用这种模式会影响性能
> 4. 如果超过15分钟,消费者没有ack,rabbitmq就会再次发送消息
# 1. ack种类
1.回复成功
`channel.basicAck(tag, false);`
2.拒绝,消息重回队列
channel.basicNack(tag, false, true);
3.拒绝,删除消息
`channel.basicNack(tag, false, false);`
# 丢弃消息
如果消费端异常,mq会一直发送消息给消费者,造成无限循环,此时:
1.指定死信队列
2.直接丢弃消息,如下配置
~~~
listener:
simple:
default-requeue-rejected: true
~~~
# 3. 消费端消息确认
1. `none`无应答,rabbitmq默认consumer正确处理所有请求。
2. `AUTO`:consumer自动应答,处理成功(注意:此处的成功确认是没有发生异常)发出ack,处理失败发出nack。rabbitmq发出消息后会等待consumer端应答,只有收到ack确定信息后才会将消息在rabbitmq清除掉。收到nack异常信息的处理方法由setDefaultRequeueReject()方法设置,这种模式下,发送错误的消息可以恢复。
3. `MANUAL`:基本等同于AUTO模式,区别是需要人为调用方法确认。
## 3.1 手动ack
1. 配置ack为手动确认
~~~
rabbitmq:
host: 192.168.56.10
port: 5672
username: tuna
password: tuna
virtual-host: vTest
# 发送确认
publisher-confirms: true
# 路由失败回调
publisher-returns: true
template:
# 必须设置成true 消息路由失败通知监听者,false 将消息丢弃
mandatory: true
listener:
simple:
# 每次从RabbitMQ获取的消息数量
prefetch: 1
default-requeue-rejected: false
# 每个队列启动的消费者数量
concurrency: 1
# 每个队列最大的消费者数量
max-concurrency: 1
# 签收模式为手动签收-那么需要在代码中手动ACK
acknowledge-mode: manual
~~~
现在队列中有21个消息
![](https://img.kancloud.cn/00/c8/00c8a8115b7b46a6befc3b7b941fc4fd_1327x464.png)
如下图启动了四个消费者,从quene1取出来四条数据,但是没有ack,同样queue2和queue3个启动了两个消费者
![](https://img.kancloud.cn/54/0a/540a61cf2d9861d301c451d871e081bc_852x251.png)
关闭服务后,因为没有ack,消息会重新进入队列,就是说一条消息也没有消费
2. 给其中一个消费者增加手动确认
~~~
@RabbitListener(queues = "queue1")
public void receive1(Message message, Channel channel) throws IOException {
System.out.println("收到死信消息A:" + new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
~~~
重启服务后,如下图,quene1的四个消费者有一个可以手动ack,所以一直成功消费,其他三条占着茅坑不拉屎
![](https://img.kancloud.cn/cc/9d/cc9d09f1cd1fc3526e0d5879232368f3_1167x377.png)
## 3.2 自动确认
~~~
acknowledge-mode: auto
~~~
重启服务,所有积压的消息都被消费掉了
![](https://img.kancloud.cn/a9/fb/a9fba2cf8fb724bc27b0a85baa37dfb4_1123x360.png)
将其中一个消费者,抛出异常
比如说现在有个场景:用户下订单,涉及到2个系统,订单系统和库存系统,2个系统通过消息队列会话。
订单系统会新增一条订单记录,然后通知消息到队列,库存系统消费队列里的消息然后去减库存操作。
现在我的问题是:如果订单系统没有问题,但是库存系统减库存的时候没有成功,这个时候应该怎么办?****
“如果订单系统没有问题,但是库存系统减库存的时候没有成功,这个时候应该怎么办?”
订单系统作为MQ的消费方,这个时候如果减库存失败,就给MQ返回处理失败的ACK,不同的MQ处理细节不同,但都大同小异,当你返回处理失败的ACK时,MQ会投递到RetryQueue进行重试,当重试次数达到一定限制时,会投递到DeadLetterQueue等待人工或CronJob进行补偿处理。
当然,这里还有潜在的另一个问题,消费重复,比如,你处理成功了,但是由于某些原因比如业务处理超时导致回给MQ了消费失败的ACK,就会导致消费重复的问题,可以在业务方用冥等方式处理。