Raft在CMQ中的三个应用早期我们在rabbitmq的基础上构建了一套可扩展的消息中间件CRMQ1.0。由于rabbitmq的GM同步算法在性能等方面存在瓶颈,所以自研了基于raft算法的内部版本CRMQ2.0和腾讯云CMQ。在保证强一致性和高可靠性的前提下,性能和易用性得到显着提升。实现采用生产Confirm+消费Ack机制,保证消息不会丢失。Confirm和Ack机制都是由raft保证的。产生的消息通过Raft转换为Entry,同步到大多数节点并提交。完成后,各节点状态机应用Entry,将消息内容写入磁盘,Leader节点向客户端回复Confirm,表示消息生产成功。消费时,客户端从Leader节点拉取消息。消费完成后,通过Ack命令通知服务端消息已被消费,可以删除。Ack请求通过Raft同步后,各节点应用该请求。消息删除后,将不再发送。下面描述详细过程:生产过程:1)生产者向Leader的Raft模块发送生产消息请求。2)Raft模块完成Entry的创建和同步。3)在大多数节点上持久化并返回成功后,条目被标记为Committed。4)所有节点的StateMachines应用日志,取出实际生产请求,将消息内容写入磁盘,更新ApplyIndex。这一步不需要刷机。5)Leader回复客户端Confirm,通知生产成功。6)如果之后重启机器,生产消息会通过raft日志恢复,保证确认的消息不会丢失。消费流程:1)消费者从Leader节点拉取消息。2)Leader接收到未删除的消息后,从磁盘中加载并传递给客户端。3)客户端处理后发送Ack报文,通知服务器删除该报文。4)Ack请求被Raft同步后标记为Committed。5)每个节点状态机应用日志,设置消息对应的位,设置为已删除并更新ApplyIndex。6)通知客户端删除成功。7)如果机器重启,通过Raft日志恢复Ack请求,保证删除的消息不会再次投递。快照管理:快照管理与业务密切相关。不同系统制作快照的成本差别很大。CMQ中的快照内容非常轻。快照需要几毫秒,平均每5分钟创建一次。每个节点独立完成。为此,在内存中维护了一个动态快照。制作快照时,先复制动态快照的副本,然后处理流程继续更新动态快照,复制的副本用于创建快照文件,不影响实际处理流程。snapshot的具体内容包括:1)term:snapshot对应的entry的term(参考算法)2)index:snapshot对应的entry的index(参考算法)3)node_info:进入时的集群配置信息。4)主题信息:每个队列一项。CMQ中同一个队列产生的消息是顺序写入并存储在分片中的,所以只需要记录最后一个分片的状态(分片文件名,文件偏移量)。5)队列信息:每个队列一项。CMQ使用bitmap记录消息的删除,在内存中维护,做快照时dump到快照文件中。可靠性:业界统一的衡量标准是RPO(RecoveryPointObjective),反映了数据在发生故障时恢复的完整性指标。由于只有提交的日志才会应用到状态机,写入时会强制刷新raft日志,所以故障重启后,可以恢复快照+raft日志,不会丢失数据,且RPO=0。但是,如2.7节所述,当Leader失效时,可能会产生重复数据,需要幂等性保证或去重机制来解决这个问题。可用性:业界统一的衡量标准是RTO(RecoveryTimeObjective),反映发生故障时服务恢复的及时性。从机故障对系统没有影响(RTO=0)。当leader失效时,其他节点自发选举新的leader,CMQ前端具有自动重连功能。当连接断开时,会自动寻找新的leader,系统会长时间不可用。减少。目前CMQ中配置的选举超时时间为2s~4s,在不考虑选举冲突的前提下,RTO上限为4s。在CMQ中,Leader通过与Follower的心跳来判断自己是否存在网络分区。当检测到分区时(现在大部分节点最后一次心跳回复的时间都在2s以上),Leader主动断开前端。前端发现后,会自动寻找新的Leader。在此期间,客户端的请求会超时。连接到新的leader后,客户端会重试之前超时的任务,后续请求恢复正常。4Raft算法性能优化Raft算法的性能瓶颈主要有两个:1)每次写入日志都需要flush才能返回成功,flush是一个耗时的操作。2)由于算法限制,所有请求都由Leader处理,所有节点无法提供服务。针对以上两个问题,我们做了如下优化:1)批处理:当请求量较大时,不是每条日志都写入磁盘,或者日志累积到一定数量后,再刷盘以集中的方式,从而减少磁盘刷新频率。相应的,在同步给follower的时候也会使用批量同步,follower接收到日志后,将日志批量写入磁盘。2)Multi-Raft:进程中同时运行多个raft实例,机器之间形成多个raftgroup,将client请求路由到不同的group,实现多master读写,提高并发性能。通过将leader分布在不同的机器上,提高了系统的整体利用率。3)async-rpc:在日志同步过程中使用同步rpc方式,另一端只能在一端处理时等待,性能较差。我们使用异步方式让leader发送和follower并发处理。发送过程中,leader维护一个发送窗口,当确认rpc数达到上限时停止发送。窗口值上限:在与同样是高可靠(多副本同步刷)的Rabbitmq的性能对比中,CMQ在相同的压测场景下,速度可以达到RabbitMQ的四倍左右。以下是TS60机器在1KB消息大小下的性能数据:CMQ在测试中使用单个Raftgroup来保证测试的公平性。监控显示CPU、内存、网卡都没有达到瓶颈,系统瓶颈是磁盘IO。iostat显示w_await比svctm大得多。主要原因是刷盘需要时间,导致写操作需要排队等待。在实际生产环境CMQ中,我们通过绑定raftgroup和disk来实现raftgroup之间磁盘的隔离。一方面保证了磁盘的顺序读写,另一方面充分利用了机器的cpu、内存、网卡等资源。5.通用Raft库CMQ完全实现了Raft算法,解决了很多细节和难点。考虑到分布式系统设计的复杂性,如果开发人员只关注与业务相关的部分,将显着降低开发难度,提高系统质量。因此,我们将CMQ的raft部分以库的形式分离出来。用户可以使用它可以构建一个强一致和高可用的分布式系统。目前,图书馆已完成基线版本的开发,并在系内投入使用。验证完成后,将对更多商家开放。6.总结消息中间件通常分为高可靠版和高性能版。CMQ是金融级高可靠分布式消息中间件,通过raft保证消息的可靠性不丢失。同时,与竞品相比,其性能和易用性有了显着提升。此外,我们自主研发的消息中间件ckafka高性能版也已经上线腾讯云。完全兼容Kafka0.09~0.10版本客户端。关于CKafka的具体技术介绍,请关注后续技术文章。Raft算法强调Leader的地位,选举和日志同步都是围绕Leader展开的。Leader负责处理所有的请求,保证系统的强一致性;Leader选举和日志同步算法保证数据可靠不丢失;另外,以上步骤只需要大部分的正常互联,大大提高了系统的可用性。机器故障不受影响。但是所有请求都由Leader处理,并没有充分利用从节点的资源。目前谷歌的Spanner已经支持从节点读取,未来我们会做进一步的研究。Raft算法易于理解和工程化,相信在未来它会被应用到越来越多的分布式系统中。原文链接:https://cloud.tencent.com/community/article/937437【本文为专栏作者《腾讯云技术社区》原创稿件,转载请联系原作者获得授权】点此查看更多关于作者的好文
