多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
![](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,就会导致消费重复的问题,可以在业务方用冥等方式处理。