在使用RabbitMQ的时候,我们可以通过消息持久化操作来解决因为服务器的异常奔溃导致的消息丢失,除此之外我们还会遇到一个问题,当消息的发布者在将消息发送出去之后,消息到底有没有正确到达broker代理服务器呢?如果不进行特殊配置的话,默认情况下发布操作是不会返回任何信息给生产者的,也就是默认情况下我们的生产者是不知道消息有没有正确到达broker的,如果在消息到达broker之前已经丢失的话,持久化操作也解决不了这个问题,因为消息根本就没到达代理服务器,你怎么进行持久化,那么这个问题该怎么解决呢?
RabbitMQ的消息确认有两种。
一种是消息发送确认。这种是用来确认生产者将消息发送给交换器,交换器传递给队列的过程中,消息是否成功投递。发送确认分为两步,一是确认是否到达交换器,二是确认是否到达队列。
第二种是消费接收确认。这种是确认消费者是否成功消费了队列中的消息。
下面的代码是消费接受确认(完成能者多劳):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
application.properties
spring.rabbitmq.host=192.168.56.101
spring.rabbitmq.port=5673
spring.rabbitmq.username=test
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/ems
# 发送确认
spring.rabbitmq.publisher-confirm-type=correlated
#spring.rabbitmq.publisher-confirms=true(旧版)
#spring.rabbitmq.publisher-confirms=true
# 发送回调
spring.rabbitmq.publisher-returns=true
# 消费手动确认
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual
#并发消费者初始化值
spring.rabbitmq.listener.simple.concurrency=1
#并发消费者的最大值
spring.rabbitmq.listener.simple.max-concurrency=10
#每个消费者每次监听时可拉取处理的消息数量
#在单个请求中处理的消息个数,他应该大于等于事务数量(unack的最大数量)
spring.rabbitmq.listener.simple.prefetch=1
# 是否支持重试
spring.rabbitmq.listener.simple.retry.enabled=true
RabbitConfig
@Configuration
public class RabbitConfig {
//队列TestDirectQueue
@Bean
public Queue TestDirectQueue() {
// durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
// exclusive:默认也是false,是否独占队列
// autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
return new Queue("TestDirectQueue",true,false,false);
}
}
生产者
@RestController
public class SendMessageController {
@Autowired
RabbitTemplate rabbitTemplate; //使用RabbitTemplate,这提供了接收/发送等等方法
@GetMapping("/sendDirectMessage")
public String sendDirectMessage() {
for(int i = 0; i<20; i++) {
String msg = "msg" + i;
rabbitTemplate.convertAndSend("", "TestDirectQueue", msg);
}
return "ok";
}
}
消费者1
@Component
@RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
public class DirectReceiver {
@RabbitHandler
public void process(String testMessage, Message message, Channel channel) throws IOException{
try {
//确认一条消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("DirectReceiver消费者收到消息1 : " + testMessage);
}catch (Exception e){
//消费者告诉队列信息消费失败
/**
* 拒绝确认消息:
* channel.basicNack(long deliveryTag, boolean multiple, boolean requeue) ;
* deliveryTag:该消息的index
* multiple:是否批量true:将一次性拒绝所有小于deliveryTag的消息
* requeue:被拒绝的是否重新入队列
*/
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}
}
消费者2
@Component
@RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
public class DirectReceiver2 {
@RabbitHandler
public void process(String testMessage, Message message, Channel channel) throws IOException{
Thread.sleep(4000);
try {
//确认一条消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("DirectReceiver消费者收到消息1 : " + testMessage);
}catch (Exception e){
//消费者告诉队列信息消费失败
/**
* 拒绝确认消息:
* channel.basicNack(long deliveryTag, boolean multiple, boolean requeue) ;
* deliveryTag:该消息的index
* multiple:是否批量true:将一次性拒绝所有小于deliveryTag的消息
* requeue:被拒绝的是否重新入队列
*/
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}
}
消费者2设置了睡眠时间,所以消费者1消费信息时间快,可以尽可能的消费没被消费的消息,达到能者多劳的实现。