概述
在使用RabbitMQ的时候,我们可以通过消息持久化操作来解决因为服务器的异常奔溃导致的消息丢失,除此之外我们还会遇到一个问题,当消息的发布者在将消息发送出去之后,消息到底有没有正确到达broker代理服务器呢?如果不进行特殊配置的话,默认情况下发布操作是不会返回任何信息给生产者的,也就是默认情况下我们的生产者是不知道消息有没有正确到达broker的,如果在消息到达broker之前已经丢失的话,持久化操作也解决不了这个问题,因为消息根本就没到达代理服务器,你怎么进行持久化,那么这个问题该怎么解决呢?
RabbitMQ为我们提供了两种方式:
- 通过AMQP事务机制实现,这也是AMQP协议层面提供的解决方案;
- 通过将channel设置成confirm模式来实现;
什么情况下需要自动确认或者手动确认呢?
- 自动确认:消息不重要的情况下可以选择
- 手动确认:消息重要的情况下,必须手动确认,防止消费端服务挂掉,消息未被消费就丢失的情况发生
开启手动确认
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
listener:
simple:
acknowledge-mode: manual # 手动确认
模拟手动确认
生产者发送消息
@SpringBootTest
class DemoApplicationTests {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void contextLoads() {
HashMap<String, Object> map = new HashMap<>();
map.put("name","杜国涛");
rabbitTemplate.convertAndSend("topicExchange","snstore.order.refound",map);
}
}
我们可以看到消息已经发布到了 snstore.order.refound 队列中
我们再来到消费端:
先模拟一下未开启确认的情况:
@RabbitListener(queues = "snstore.order.refound")
@RabbitHandler
public void orderRefoundHandler(@Payload Map order, Message message, Channel channel) {
log.info("接受订单退款 >>退款订单详情:{}",order);
}
可以看到消息已经接受到了,但是来到rabbitmq管理界面可以看到,消息并未被确认,还会保留在队列当中。当该消费者重启时还会继续消费该消息,直至确认为止。从一方面可以有效的保障了消息的安全性
开启手动确认,重新启动项目
@RabbitListener(queues = "snstore.order.refound")
@RabbitHandler
public void orderRefoundHandler(@Payload Map order, Message message, Channel channel) {
log.info("接受订单退款 >>退款订单详情:{}",order);
try {
// TODO 业务逻辑
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); // 手动确认消息
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
当项目启动后可以发现,已接收到了队列中的消息。
再来到rabbitmq的管理界面可以看到消息已被确认
最后补充一下知识:
rabbitmq 发送消息与接收消息实现自动序列化与反序列化的配置
@Configuration
public class RabbitMqConfig implements RabbitListenerConfigurer {
@Autowired
public ConnectionFactory connectionFactory;
@Bean
public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
// 这里的转换器设置实现了 通过 @Payload 注解 自动反序列化message body
factory.setMessageConverter(new MappingJackson2MessageConverter());
return factory;
}
@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(myHandlerMethodFactory());
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrentConsumers(3);
factory.setMaxConcurrentConsumers(10);
// 手动确认消息
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return factory;
}
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
// 这里的转换器设置实现了发送消息时自动序列化消息对象为message body
template.setMessageConverter(jsonMessageConverter());
return template;
}
}