本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、生产者消息确认
步骤:
-
创建spring-confirm-mq项目
- web/lombok/mq
-
初始化队列和交换机信息
-
编写配置文件
-
发送消息测试
解决办法:
- 第一步消息没有正确到达交换机-----》confirm确认模式解决
- 第二步消息没有正确的路由至队列----》return退回模式解决
解决步骤:
- 在配置文件中开启确认模式、退回模式
- 编写类实现RabbitTemplate.ConfirmCallBack、RabbitTemplate.ReturnCallBack接口并且重写方法
- 将重写的方法引用到RabbitTemplate中
1. 代码实现
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
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;
import javax.annotation.PostConstruct;
/**
* <dl>
* <dd>描述: ~ RabbitMqConfirm</dd>
* <dd>创建时间: 9:31 2020/9/28</dd>
* <dd>创建人: guodong</dd>
* <dt>版本历史: </dt>
* <pre>
* Date Author Version Description
* ------------------------------------------------------------------
* </pre>
* </dl>
*/
@Component
@Slf4j
public class RabbitMqConfirm implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct //会在@Autowired后执行
public void init(){
rabbitTemplate.setConfirmCallback(this::confirm);
rabbitTemplate.setReturnCallback(this::returnedMessage);
}
/**
* @Date: 9:33 2020/9/28
* @Parms [correlationData, ack, cause]
* @ReturnType: void
* @Description: 确认消息是否可以到达交换机
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(ack){
log.info("消息正确到达交换机, ack {}", ack);
}else {
log.info("消息到达交换机失败, ack {}, cause:{}", ack, cause);
}
}
/**
* @Date: 9:34 2020/9/28
* @Parms [message, replyCode, replyText, exchange, routingKey]
* @ReturnType: void
* @Description: 只有消息从交换机路由至队列失败时会调用
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
log.error("消息从交换机路由至队列失败: replyCode: {}, replyText: {} exchange: {} routingKey:{} ",
replyCode, replyText, exchange, routingKey);
}
}
spring:
rabbitmq:
host: 192.168.200.132
#开启确认模式
publisher-confirms: true
#开启退回模式
publisher-returns: true
二、消费消息确认
1. ack确认三种模式:
-
manual: 手动确认
- 需要手动确认消息可以删除还是重新回到队列
-
auto: 自动确认
- 消息一旦消费异常,那么消息会自动重回队列进行排队等待消费,如果大批量消息消费失败会造成消息的积压,从而影响消息正常消费
-
none: 不需要确认
- 不需要确认,一旦消费者接收到消息后自动将消息从队列删除
2. 手动确认模式代码实现
步骤:
- 在配置文件中开启手动确认
- 修改消费消费业务逻辑
3. 代码实现
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
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;
/**
* <dl>
* <dd>描述: ~ OrderAListener</dd>
* <dd>创建时间: 9:21 2020/9/28</dd>
* <dd>创建人: </dd>
* <dt>版本历史: </dt>
* <pre>
* Date Author Version Description
* ------------------------------------------------------------------
* 2020/9/28 guodong 1.0 1.0 Version
* </pre>
* </dl>
*/
@RabbitListener(queues = "order.A")
@Component
@Slf4j
public class OrderAListener {
@RabbitHandler
public void receiveMsg(String msg,
Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag,
@Header(AmqpHeaders.REDELIVERED) boolean redelivered){
log.info("orderA接收到消息: {}, deliveryTag: {}, redelivered: {}", msg, deliveryTag, redelivered);
try {
if(msg.contains("苹果")){
throw new RuntimeException("爱国就不要买苹果手机!!!");
}
/*
第一个参数是: 消息的位置
第二个参数是: 是否要批量确认,如果设置了批量确认那么在进行消息确认删除时会将小于
该消息位置的消息全部删除,造成别的消费逻辑的变化。所以一般设置为false
*/
channel.basicAck(deliveryTag, false);
}catch (Exception e){
if(redelivered){ //如果参数为true标识重回过队列
try {
/*
channel.basicReject: 单条消息拒绝,第一个参数是消息的位置,第二个参数是标识是否重写回到队列
channel.basicNack: 多了一个可以批量拒绝的参数,但是一般不使用批量,所以使用basicReject就可以。
*/
channel.basicReject(deliveryTag, false);
//channel.basicNack(deliveryTag, false, false);
log.info("消息已经重新回到过队列并且再次消费失败,拒绝重新再次回到队列, redelivered: {}", redelivered);
} catch (IOException e1) {
e1.printStackTrace();
}
}else { //第一次消费失败,没有重新回到过队列,应该给一次机会
try {
channel.basicReject(deliveryTag, true);
log.info("消息第一次消费失败,重新回到队列, redelivered: {}", redelivered);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
spring:
rabbitmq:
host: 192.168.200.132
#开启确认模式
publisher-confirms: true
#开启退回模式
publisher-returns: true
listener:
simple:
acknowledge-mode: manual #手动确认 auto #自动确认 none #不需要确认
4.小结
-
在配置文件中开启消息的手动确认
-
在消费者里面编写业务逻辑代码
-
Channel channel : 用于进行消息的确认
-
@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag: 消息的位置
-
@Header(AmqpHeaders.REDELIVERED) boolean redelivered: 标识消息是否重回过队列,如果重新回到过那么为true
-
用于手动进行消息确认的方法
-
参数介绍
- deliveryTag: 消息的位置
- multiple: 是否批量确认,批量确认会造成小于该消息的消息被确认删除
- requeue: 是否重回队列
-
channel.basicAck(long deliveryTag, boolean multiple)
-
channel.basicNack(long deliveryTag, boolean multiple, boolean requeue)
-
channel.basicReject(long deliveryTag, boolean requeue)
-
-