本文作者:刘树东-同程-艺龙技术专家01/使用概述我们倾向于选择Java实现的没有任何第三方依赖的MQ;久经考验:RocketMQ经历了阿里双11的考验,性能和稳定性得到了充分验证;功能实用:RocketMQ的发送端提供同步、异步、单边、延时发送功能;消费端具有重试队列、死信队列和消息重置功能,非常方便实用。基于以上三点,我们选择了RocketMQ。RocketMQ在同程的使用场景包括削峰、解耦、异步处理、数据同步等。目前已广泛应用于公司各大业务线的核心系统,并承受了微信入口的巨大流量,每天消息量1000亿+。同程RocketMQ框架图如上。机票、交通、酒店三大业务线通过JavaSDK和HttpProxy接入MQ集群。其中HttpProxy主要是为了方便其他语言的客户端;同时,为了更好的实现资源隔离,我们按照业务线对后端服务器节点进行物理隔离,最大程度保证业务线之间不会相互影响。整个MQ集群通过MQ服务平台进行管理。02/同城双中心同城双中心主要是为了实现以下三个目标:保证单机故障时的业务可用性,保证数据的可靠性和水平扩展,外部用户通过LVS请求调用服务和应用Nginx,应用底层主要调用MySQL、Redis、MQ,双中心分为同城冷备和同城双活两种方案。同城冷备有两个MQ集群,分别是源集群和目标集群。生产者将消息生产到源集群,然后通过消息同步集群将源集群中的元数据如主题、消费者组、消息等同步到目标集群。源集群和目标集群中的消费者都可以消费。在同一个城市,我们建立了一个跨中心的全球MQ集群。用户流量分布到两个中心,每个业务将产生的消息写入自己中心的MQ。同时,其他服务和应用程序将使用来自该中心的所需消息。当单个机房发生broker节点故障时,每组broker的两个slave位于不同的机房,至少有一个slave数据几乎全满;消息生产会跨机房到另一个机房,另一个机房的消费者继续消费。简单对比,同城冷备方案需要source和target两个集群,资源使用率不高,需要同步topic、消费组、消费进度等元数据;同城双活方案资源使用比较合理,业务流量在哪里?,其中生成和使用消息。同城双活有以下三个需求:①就近生产:生产端在A机房,生产消息也存储在A机房的broker中。②最近消费者:如果消费者在机房A,消费消息也来自A机房broker。③broker主节点故障可以实现自动主选举。就近生产和就近消费的核心问题是如何识别客户端和服务器是否在同一机房。识别客户端所在机房有两种方式:一是获取客户端IP,然后通过第三方组件解析IP所属机房;第二,配合运维人员在每个容器或物理机中设置IP地址环境变量,通过读取环境变量来感知客户端所在的机房。比如从环境变量中读取逻辑机房的ID或者逻辑机房的名称。服务器机房的识别方式有3种:一是通过IP查询,解析IP段属于哪个机房;第二,服务器节点向元数据节点注册时,在协议层添加机房标识;第三,将经纪人名称添加到房间ID。就近生产原则:优先就近生产,当就近生产不能满足要求时,可跨机房生产。如果机房后端的服务节点可用,则生产者将消息生产到各自的机房。如果机房所有broker节点都不可用,生产端会把消息投递给另一个centralbroker。当两个机房的消费端正常时,每个机房的消费端只会消费本机房的消息。如上图所示,如果idc2的消费端全部失效,则所有消息都会被另一个机房idc1的消费端消费。为了实现就近消费,我们实现了一个机房队列分配算法:来自各个机房的消息会平均分配到本机房的消费端;如果机房没有消费端,则将消息平均分发到其他机房的消费端。故障切换的实现步骤如下:第一步:Nameserver基于Raft协议选主;Step2:所有broker向Nameserver领导节点注册,leader节点将broker的注册信息同步给其他Nameserver节点;Step3:brokermaster节点异常时,Nameserver负责从剩余的broker节点中重新选举master节点;Step4:此时生产端无法向旧的brokerleader发送消息。如果旧broker的主节点复活,会自动切换到从节点。Nameserver元数据系统一主多从,broker一主二从。如果master失效,Nameserver元数据系统会根据一定的条件,比如同步进度或者版本号,从slave1和slave2中选择一个新的master。上图表示slave2被选为master。然后新的主人将注册到名称服务器元数据系统并+1版本号。如果老master复活了,会因为版本号小于新master的版本号而被砍为slave。在生产环境中,实际操作了双中心钻。具体过程如下:1)14:30切域名,流量切到某机房(二中心)。三四分钟后,第二中心的流量已经超过了第一中心,而第一中心的流量接近于零;2)15:00左右,两中心交通恢复;3)15:30左右,流量切换回第一中心。可以明显看出,当流量切到第一中心时,第二中心的消息产量趋于零。总体来说,两个中心的排练都比较顺利,新闻的制作也顺理成章。简单总结一下,双中心方案建立了一个跨机房的全局MQ集群。生产和消费都遵循先后顺序和就近原则。配置一主两从。如果一主一从成功写入一条消息,则认为该消息成功。Nameserver通过Raft协议选主。选出master后,会监控各组broker的leader节点。如果broker的leader节点发生故障,将从该组中剩余的slaves中选出一个新的master节点。03/平台治理平台治理非常重要。它可以使环境更加稳定和可靠。当出现异常时,可以及时向用户发送告警,当出现故障时,可以快速定位。MQ平台治理分为主题消费组治理、客户端治理和服务端治理。主题消费组的使用遵循先申请后使用的原则。当出现问题时,可以根据主题和消费组的申请者找到主题。MQ环境有多种类型。您可以一键申请所有环境,也可以只限制生产环境申请,释放其他环境申请。对于主题,我们限制了每个主题的最大生产速度,以防止单个主题的生产速度过快而影响其他主题的生产。当消息累积到一定阈值时,可以向业务方发送告警,通知业务方处理;如果消息长时间不处理,可以取消消费组的消费权限或者重置消费进度,避免大量消息堆积影响生产和消费。如果消费者节点下线一定时间没有恢复上线,可以快速向业务方发出告警,通知业务方及时处理。客户端治理主要是做一些统计和检测工作,会统计每条消息的生产和消费耗时,消息的大小,重复消费的次数。如果生产/消费者版本过低或者当前版本有bug,也可以通知业务方。这一系列的统计和检测最终需要服务于消息的全链路跟踪。统计和检测会记录消息相关的信息,包括每条消息什么时候从哪个服务IP和端口号发送到服务器的哪个节点,哪个服务的消费者什么时候消费,消费消费等。时间、重复消费次数等信息,可轻松查询所需信息。服务器治理负责集群自我保护、集群健康检查、集群性能检查和集群高可用。集群的自我保护主要针对生产和消费,特别是在消息生产速度过快或大量消息积压的情况下;性能检测可以通过实时发送一批不同大小的消息来检测生产和消费;集群高可用指标当故障发生时,服务平台可以及时发出通知。通过几张图来展示实际的治理,在MQ服务平台上,可以申请在线、离线以及topic和consumer组的权限。上图展示了每分钟各集群的消息生产消耗和累积曲线。MQ平台还提供了集群监控功能,可以查看当前集群topic、消费者组织数量、消息的intps、outtps、broker数量。同时可以查看各个broker的消息生产和消息积压排名,方便出现故障时排查问题。在使用RocketMQ的过程中,我们也踩过不少坑。1)升级双中心版本时,没有考虑版本的向下兼容,队列的分配算法有问题,导致部分消息重复消费,部分消息没有消费;2)某个broker上的topic消费群如果数量过多,会导致注册Nameserver的时间过长。如果多个broker上有多个topic消费者组,会导致broker内存OOM;3)由于主题长度判断不一致,重启服务器时会丢失少量消息;4)broker进程会卡顿,后来确认是OS版本问题。可以通过升级os版本来解决。04/未来展望同程出行对于RocketMQ的未来规划分为以下三个方面:第一,历史数据归档。很多业务方对数据回溯的周期要求比较长,线上生产环境中消息的实际存储时间可能只有2-3天。我们会考虑从历史存档数据中查询;第二,底层存储剥离,计算和存储分离。目前新版社区已经支持该能力;第三,希望利用积累的历史数据帮助业务完成更多的数据预测,比如订单量预测、退改数据等,可以更好的帮助业务解决问题,同时提供商务机提供扩缩容支持。
