在游戏陪玩app开发中,随着业务的不断复杂和调用链路的不断增长,我们可能会慢慢引入越来越多的中间件来更好的服务于我们的系统,但是每样技术都是一把双刃剑,在提高游戏陪玩app开发系统性能的同时,也要想办法来减少它对系统带来稳定性的影响,今天要带来的是如何让RabbitMQ的可靠性达到保证。
要想了解如何保证RabbitMQ的可靠性,首先要从它的执行流程开始了解。
执行流程
由执行流程就可以看到,消息不管是在生产者发送到MQ服务器的过程中或者是在消费的过程中都存在着丢失的风险,那怎么办呢?
消息确认机制-可靠抵达
事务
提到保证可靠性的问题,小伙伴们肯定首先可以想到的是事务机制,RabbitMQ也提供了事务消息,不过官方文档也写到事务消息让MQ的性能下降250倍,所以说在游戏陪玩app开发对性能要求很高的情况下,显然不适合去使用事务消息。
那只能从其他几个方面来保证可靠性了。
这是一张消息发送到消费的简图,要保证可靠性的话,要从三个方面来来考虑。
publisher → Broker,confirmCallback机制
- 在创建connectionFactory的时候设置publisherConfirm(true)选项,开始confirmCallback。
- 消息只要被broker收到就会执行confirmCallback,如果是cluster模式,需要所有broker都接收到才会调用confirmCallback。
- 被broker接收到只能表示message已经抵达服务器,并不能保证消息一定被投递到目标queue里。所以需要用到接下来的returnCallback。
#配置yml文件
spring:rabbitmq:#开启发送单确认publisher-confirms: true
@PostConstruct
public void initRabbitTemplate(){rabbitTemplate.setConfirmCallback(((correlationData, ack, cause) -> {log.info("confirm---->correlationData{},-------->ack{},-------->cause{}",correlationData,ack,cause);}));
}
Exchange → Queue,returnCallback 机制
- confirm模式只能保证消息抵达broker,不能保证消息准确投递到目标queue里。在游戏陪玩app开发的一些业务场景下,需要保证消息一定要投递到目标queue里,此时就需要用到return退回模式。
- 这样如果未能投递到目标queue里将会调用returnCallback
,可以记录下详细到投递数据,定期的巡检或者自动纠错都将需要这些数据。
#配置yml文件
spring:rabbitmq:#开启发送端消息抵达队列确认publisher-returns: true#只要抵达队列,以异步方式优先回调这个returnconfirmtemplate:mandatory: true
@PostConstruct
public void initRabbitTemplate(){rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {log.info("return---->message{},-->replyCode{},-->replyText{},-->exchange{},-->routingKey{}",message,replyCode,replyText,exchange,routingKey);});
}
Queue → Consumer,ack消息确认机制(消费端)
#配置yml文件
spring:rabbitmq:#切换为手动acklistener:direct:acknowledge-mode: manual
完成以上三个配置后,我们用于消费消息的代码就会变成这样
@Service
@Slf4j
@RabbitListener(queues = "demo.queue")
public class DemoListener {@Autowiredprivate DemoService service;@RabbitHandlerpublic void listener(DemoEntity entity, Channel channel, Message message) throws IOException {log.info("-----------开始消费消息----------");try {service.doSomething(entity);channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);} catch (Exception e) {channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);}}
}
当然在游戏陪玩app开发中仅仅是这三步配置还是不够的,因为上述步骤并没有做到消息持久化,在做好持久化方案后,我们的消息将无敌。(夸张的修辞手法)
小结:
做好这些后,游戏陪玩app开发中消息丢失的可能性已经很小很小了,但是又会有新的问题出现,比如说重复消费,消息过多导致消费者宕机等。
防止消息重复
消息积压