RabbitMQ如何保证消息可靠性?
RabbitMQ如何保证消息可靠性?
为什么会提到MQ的消息可靠性?
线上环境中,最近偶尔出现了这样的情况:业务执行过程中突然被中断了,后面的不再执行。问题定位到了MQ生产消息的位置。
一、 如何保证消息的可靠性?
要保证消息的可靠性,就需要保证消息在流转的每一个阶段都能够保证可靠性。
一个消息会经历下面四个阶段:
1. 生产者发出后保证到达了MQ。
2. MQ收到消息保证分发到了消息对应的Exchange。
3. Exchange分发消息入队之后保证消息的持久性。
4. 消费者收到消息之后保证消息的正确消费。
经历了这四个保证,我们才能保证消息的可靠性,从而保证消息不会丢失。
二、 生产者发送消息到MQ失败?
我们的生产者发送消息之后可能由于网络闪断等各种原因导致我们的消息并没有发送到MQ之中,但是这个时候我们生产端又不知道我们的消息没有发出去,这就会造成消息的丢失
为了解决这个问题,RabbitMQ引入了事务机制和发送方确认机制(publisher confirm)
1. 事务机制
RabbitMQ 支持使用事务,在没有生产者确认之前,保证消息不丢失的唯一方法就是使用事务,是 channel 具有事务性,然后该 channel 上的消息都具有事务性。但是事务会将 RabbitMQ 的吞吐量降低到250倍,由于事务机制过于耗费性能,所以一般不用,这里我着重讲述发送方确认机制
2. 发送方确认机制
这个机制很好理解,就是消息发送到MQ那端之后,MQ会回一个确认收到的消息给我们
开启confirm:
$channel->confirm_select();
注册ack/nack回掉方法:
1 //ack callback function 2 $channel->set_ack_handler(function (AMQPMessage $message){ 3 echo 'ack' . $message->getBody() . PHP_EOL; 4 }); 5 //nack callback function 6 $channel->set_nack_handler(function (AMQPMessage $message){ 7 echo 'ack' . $message->getBody() .PHP_EOL;
8 });
设置ack/nack超时时间:
$channel->wait_for_pending_acks_returns(5);//set wait time
3. 下面摘取hyperf中amqp组件中生产消息的代码
1 private function produceMessage(ProducerMessageInterface $producerMessage, bool $confirm = false, int $timeout = 5) 2 { 3 $result = false; 4 5 $this->injectMessageProperty($producerMessage); 6 7 $message = new AMQPMessage($producerMessage->payload(), $producerMessage->getProperties()); 8 $connection = $this->factory->getConnection($producerMessage->getPoolName()); 9 10 try { 11 if ($confirm) { 12 $channel = $connection->getConfirmChannel(); 13 } else { 14 $channel = $connection->getChannel(); 15 } 16 17 $channel->set_ack_handler(function () use (&$result) { 18 $result = true; 19 }); 20 $channel->basic_publish($message, $producerMessage->getExchange(), $producerMessage->getRoutingKey()); 21 $channel->wait_for_pending_acks_returns($timeout); 22 } catch (\Throwable $exception) { 23 isset($channel) && $channel->close(); 24 throw $exception; 25 } 26 27 if ($confirm) { 28 $connection->releaseChannel($channel, true); 29 } else { 30 $result = true; 31 $connection->releaseChannel($channel); 32 } 33 34 return $result; 35 }
注意: 事务机制和普通confirm方式的吞吐量都很低,在实际生产环境中推荐使用批量confirm和异步confirm
三、MQ接收失败或者路由失败
四、消息入队后MQ宕机
到这一步基本都是一些很小概率的问题了,比如MQ突然宕机了或者被关闭了,这种问题就必须要对消息做持久化,以便MQ重新启动之后消息还能重新恢复过来
消息的持久化要做,但是不能只做消息的持久化,还要做队列的持久化和Exchange的持久化。
五、消费者无法正常消费
参考链接:
https://segmentfault.com/a/1190000023736395