一、背景介绍
公司以前大部分服务在私有云上,因使用有一段时间了,机器比较老化再加上运维成本高,计划将整个机房上云,因负责中间件一块,所以最近将RabbitMQ顺利地迁移到云上。
先说下大概部署结构,为 保证高可用,在私有云上部署了3台Broker,应用在配置文件中直接配置3个IP,每次请求时由客户端随机选择1台。
本次迁移的目标是:
1、零数据丢失,但不保证消息不重复消费;
2、不出现整个MQ集群长时间(2分钟以上)不可用;
二、方案分析
关于数据丢失,我们先要知道RabbitMQ中有哪些数据:
Exchange、Queue、Message。
关于Exchange和Queue,可以设置在不存在的时候创建,但这样难以管控,所以一般我们都是在后台由管理员创建Exchange和Queue,并设置好相应属性,一般来说都要设置为持久化,即durable为true,这样保证在Broker重启时Exchange和Queue还存在。
Message的持久化则要在发送的时候设置相应的参数,如果用的amqp-client这个包,则代码如下:
代码语言:javascript复制 channel.basicPublish(exchange, routingKey, basicProperties, payload);
其中为basicProperties为消息属性,类型为AMQP.BasicProperties
代码语言:javascript复制public static class BasicProperties extends com.rabbitmq.client.impl.AMQBasicProperties {
private String contentType;
private String contentEncoding;
private Map<String,Object> headers;
private Integer deliveryMode;
private Integer priority;
private String correlationId;
private String replyTo;
private String expiration;
private String messageId;
private Date timestamp;
private String type;
private String userId;
private String appId;
private String clusterId;
关注deliveryMode属性,要设为2消息才会持久化。
再来分析下有哪些场景下可能导致数据丢失:
1、单机宕机无法启动
假设机群有3台Broker:A、B、C,发送消息的机制如下:
如果当前请求落到A上,则消息中会保存在A这个节点上,持久化也只持久化到这台机上。
如果消息还没有被消费,这个时候A宕机,则这条消息就丢了。
针对这种情况,官方推荐用镜像队列的方案,这时消息发送过程如下:
消息先发送到队列所在Master机器A,然后A将消息同步到所有其它机器上。
这时即使A宕机了,整个集群会做漂移,将这个列列的Master漂移到另外1台机器上,因为在发送的时候消息已经同步到所有其它机器上了,因此消息不会丢失,但有可能重复消费,这就需要业务做幂等处理。
镜像队列添加命令如下:
代码语言:javascript复制Virtual host : "/oneplus"
Name : "all_ha"
Pattern : "^"
Apply to : "Queues"
Priority : 0
ha-mode : all
ha-sync-mode : automatic
2、脑裂
这个我们在生产上碰到过,可以加上以下配置避免:
代码语言:javascript复制{cluster_partition_handling, pause_minority}
另外在迁移过程中注意几点:
1)、尽量保证集群总机器数为奇数;
2)、尽量减少跨机房集群存在的时间;
那么除以做为以上我们是不是可以高枕无忧地说整个迁移万无一失了呢,上面只是讲了技术的原因,我们还要来通过其它方面来保障过程的稳定性:
1、测试
搭建整套的环境测试,以验证整个方案的有效性。
2、做好故障预案
A、备份好元数据
可以在RabbitMQ管理界面后台操作。
B、核心流程必须有数据核对方案
因为我们是电商,所以要保证整个交流主流程上的MQ消息不能丢失,所以需要有一套数据核对及补偿的方案,这个就不在这里详述了。
三、迁移过程
最后说下我们整个迁移的过程
1、第一步就是当前状态
2、在新机器房添加4台Broker
在每台机器上执行命令如下:
代码语言:javascript复制rabbitmqctl stop_app
rabbitmqctl join_cluster rabbit@${server1}
rabbitmqctl start_app
3、下线新、老机房各一台Broker,保证总数仍为奇数
先在要下线的机器上执行命令:
代码语言:javascript复制rabbitmqctl stop_app
然后在存活的节点上执行命令
代码语言:javascript复制rabbitmqctl forget_cluster_node rabbit@${server1}
4、最后下掉老机房剩下机器
命令同上
最后总结下,我们从以下几方面保证迁移过程的稳定性:
1、配置好各项参数,保证MQ内部各数据尽量不丢失
主要做了以下几方面的工作:配置好镜像队列、防止脑裂;
2、在测试环境做好充分的验证;
3、保证主流程消息有核对及补偿方案。