RabbitMQ 消息可靠性和服务高可用
消息队列
消息队列是典型的生产者消费者模型,本质就是一块可供读写消息的缓冲区。
消息队列可以解耦,异步,削峰。是现代应用开发中不可或缺的技术。
RabbitMQ
RabbitMQ是一个由erlang开发的消息队列。并发能力很强,性能极其好,延时很低,达到微秒级。
设计结构如下:
消息可靠性
RabbitMQ提供了以下方法保证消息的可靠性:
1.生产者确认机制
生产者可以通过生产者确认机制来知晓消息是否成功到达broker。
// 消息是否成功发送到Exchange final RabbitTemplate.ConfirmCallback confirmCallback = (CorrelationData correlationData, boolean ack, String cause) -> { log.info("correlationData: " + correlationData); log.info("ack: " + ack); if(!ack) { log.info("异常处理...."); } }; rabbitTemplate.setConfirmCallback(confirmCallback);
2.消费者手动确认
消费者可以设置手动确认,在手动返回ack之前,broker会一直保存该消息。
@RabbitListener(queues = RabbitMqConfig.MAIL_QUEUE) public void onMessage(Message message, Channel channel) throws IOException { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } long deliveryTag = message.getMessageProperties().getDeliveryTag(); //手工ack;第二个参数是multiple,设置为true,表示deliveryTag序列号之前(包括自身)的消息都已经收到,设为false则表示收到一条消息 channel.basicAck(deliveryTag, true); System.out.println("mail listener receive: " + new String(message.getBody())); }
3.持久化
RabbitMQ支持持久化,保证服务重启后消息不丢失。
一般交换机和队列都是要持久化的。如果消息需要持久化,可以设置delivery mode = 2。
4.路由不可达处理
RabbitMQ支持生产者监听回调函数和备份交换机两种处理方式,来保证消息不丢失。
// 回调函数 final RabbitTemplate.ReturnCallback returnCallback = (Message message, int replyCode, String replyText, String exchange, String routingKey) -> log.info("return exchange: " + exchange + ", routingKey: " + routingKey + ", replyCode: " + replyCode + ", replyText: " + replyText); rabbitTemplate.setReturnCallback(returnCallback);
备份交换机alternate-exchange 是一个普通的exchange,当你发送消息到对应的exchange时,没有匹配到queue,就会自动转移到备份交换机对应的queue,这样消息就不会丢失。
服务高可用
镜像队列集群
当MQ发生故障时,会导致服务不可用。引入RabbitMQ的镜像队列机制,将queue镜像到集群中其他的节点之上。如果集群中的一个节点失效了,自动地切换到镜像中的另一个节点以保证服务的可用性。
网络分区
fedration
Federation 插件可以让多个交换器或者多个队列进行联邦。一个联邦交换器(federatedexchange)或者一个联邦队列(federated queue)接收上游(upstream)的消息。这里的上游是指位于其他 Broker 上的交换器或者队列。
fed生产同步。避免生产跨机房的延时。
fed消费同步。有效利用多机房资源。
shovel的设计与federation相似,这里不再赘述。
容灾方案
多集群 + 多机房 + fed link相互备份。
保证如果单节点/单集群/单机房出现问题,仍然高可用。
避免了跨机房请求,也可以利用机房资源并行消费。