消费者端
目录结构
导入依赖
修改yml
业务逻辑
自动确认
手动确认
消费者端
目录结构
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies>
修改yml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
listener:
simple:
acknowledge-mode: auto
可以看出来listener由两种类型:direct、simple。前者用于直接交换机,而后者用于其他交换机
可以看出自动确认模式有三种方式:none、auto、manual。
acknowledge-mode: none 表示自动确认消息(默认值)
acknowledge-mode: manual 表示手动确认消息
acknowledge-mode: auto 表示根据异常情况确认消息
自动确认是指当消息一旦被消费者接收到则自动确认收到,并将消息从RabbitMQ的消息缓存中移除。但是在实际业务当中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果采用了手动确认方式则需要在业务处理成功后,调用channel.basicACK(),如果出现异常则调用channel.basicNack(),让其自动重新发送消息。
业务逻辑
自动确认
/**
* 消费者自动确认消息
* 1、yml中需要配置acknowledge-mode: none
* 2、消费者监听实现MessageListener
*/
@Component
public class AckListener implements MessageListener {
@RabbitListener(queues = "test_queue_name")
@Override
public void onMessage(Message message) {
System.out.println("消费者接受的消息为:" + new String(message.getBody()));
}
}
手动确认
/**
* 消费者手动确认消息
* 1、yml中需要配置acknowledge-mode: manual
* 2、消费者监听实现ChannelAwareMessageListener
*/
@Component
public class AckListener implements ChannelAwareMessageListener {
@RabbitListener(queues = "test_queue_name")
@Override
public void onMessage(Message message, Channel channel) throws Exception {
// 消息的唯一标识id
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
System.out.println("消费者接受的消息为:" + new String(message.getBody()));
// 手动签收的第一个参数为消息的唯一标识id、第二个参数表示是否批量签收
channel.basicAck(deliveryTag,true);
} catch (Exception e){
// 手动拒绝签收的第一个参数为消息id、第二个参数表示是否批量签收
// 第三个参数消息是否重回队列
channel.basicNack(deliveryTag,true,true);
System.out.println("消息被拒绝签收了");
}
}
}
/**
* 消费者手动确认消息
* 1、yml中需要配置acknowledge-mode: manual
* 2、消费者监听实现ChannelAwareMessageListener
*/
@Component
public class AckListener implements ChannelAwareMessageListener {
@RabbitListener(queues = "test_queue_name")
@Override
public void onMessage(Message message, Channel channel) throws Exception {
// 消息的唯一标识id
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
System.out.println("消费者接受的消息为:" + new String(message.getBody()));
int i = 1 / 0;//业务逻辑故意出错进入catch块
// 手动签收的第一个参数为消息的唯一标识id、第二个参数表示是否批量签收
channel.basicAck(deliveryTag,true);
} catch (Exception e){
// 手动拒绝签收的第一个参数为消息id、第二个参数表示是否批量签收
// 第三个参数消息是否重回队列
channel.basicNack(deliveryTag,true,true);
System.out.println("消息被拒绝签收了");
}
}
}
注意哈:上面的终端出现了死循环是因为channel.basicNack()方法的第三个参数使其消息重回队列中重新发送,但try的业务逻辑块一直有问题,所以会重复的报错,如果第三个参数是false结果如下。