1.介绍
在本文的前一部分“微信技术共享:微信群发即时聊天消息序列号生成实践(算法原理)”中,介绍了seqsvr的算法原理、架构核心思想以及随着业务量的快速增长而产生的架构演进。本文将介绍seqsvr分布式灾难恢复体系结构的演变。
正如我们所知,在大多数情况下,对于互联网后台系统没有独特和完美的解决方案,相同的需求甚至可能在不同的环境中演变成两种完全不同的体系结构。由于架构是可变的,单纯谈论架构并没有什么特别的意义。在此,我们系统地分享微信消息序列号生成器seqsvr容灾设计中的一些想法和权衡,希望能对大家有所帮助。
推荐:融云团队分享的消息标识生成文章也值得一读“融云技术共享:解密融云即时通讯产品的聊天消息标识生成策略”,美团的这篇文章也不错“美团技术共享:深度解密美团分布式标识生成算法”。
这篇文章是题为“即时消息识别技术专题”系列文章的第二篇。专题的一般内容如下:
“即时消息标识技术专题(一):微信海量即时消息聊天消息序列号生成实践(算法原理)”(*本文)
"即时消息标识技术专题(二):微信群发即时消息聊天消息序列号生成实践(灾难恢复方案)"
即时消息标识技术主题(3):在融云解密即时消息产品的聊天消息标识生成策略
即时消息标识技术专题(四):美团分布式标识生成算法的深度解密
即时消息标识技术主题(5):开源分布式标识生成器的技术实现
2.这篇文章分为几章
第一部分:“微信技术共享:微信的海量即时聊天信息序列号生成实践(算法原理)”
下一部分:“微信技术共享:微信的海量即时聊天信息序列号生成实践(灾难恢复方案)”(本文)
3.关于作者
曾秦颂:微信高级工程师,负责微信基础设施、微信翻译引擎、微信围棋凤凰网,致力于高可用性、高性能后台系统的设计研发。2011年毕业于西安电子科技大学,之前在腾讯搜搜从事搜索架构和分布式数据库工作。
4.灾难恢复方案的体系结构原则
接下来,我们将介绍seqsvr的容灾架构。众所周知,在大多数情况下,后台系统没有唯一和完美的解决方案,相同的需求甚至可能在不同的环境中演变成两种完全不同的体系结构。因为建筑是可变的,所以纯粹谈论建筑没有什么意义。我还将谈谈seqsvr灾难恢复设计中的一些想法和权衡,希望对大家有所帮助。
Seqsvr的灾难恢复模型在五年内经历了相对较大的重建,提高了可用性和机器利用率。
无论是在重建之前还是之后,Seqsvr始终遵循两个建筑设计原则:
1)保持你自己的结构简单;
2)避免强烈依赖外部模块。
这两点是基于seqsvr的可靠性。毕竟,seqsvr是一个与整个微信服务器的正常运行密切相关的模块。根据我们对世界的理解,系统的复杂性通常与可靠性成反比。获得一个可靠的系统的一个关键点是使它变得简单。我相信我们周围有一些这样的例子。在设计方案中有许多高而复杂的东西,它们总是能被看到静静地填充一些高的洞。当然,简单的系统并不意味着低劣。我们要做的是理清核心点,然后在满足这些核心点的基础上提出一个足够简单的解决方案。
那么,seqsvr的核心是什么?每个uid的序列应用程序应该递增,而不是回滚。在这里,我们发现如果seqsvr满足这样一个约束,即任何uid在任何时候都只有一个AllocSvr来提供服务,那么它就可以很容易地实现序列增量的要求,而不需要回归。
即时消息标识技术专题(2):微信群发即时消息序列号生成实践(灾难恢复方案)_1.jpg
▲图1:两个AllocSvr服务的相同uid导致的序列回退。客户端读取的序列是101、201和102
然而,由于这一限制,多个AllocSvr同时服务于同一网段的多主机模型在这里不适用。我们只能采用单点服务的模式。当AllocSvr的服务不可用时,该机器所服务的uid段被切换到其他机器以实现容灾。需要引入仲裁服务来探测AllocSvr的服务状态,并决定哪个AllocSvr应该加载每个uid段。为了可靠性,仲裁模块不直接操作AllocSvr,而是将加载配置写入StoreSvr持久性,然后AllocSvr定期访问StoreSvr以读取最新的加载配置并决定其自己的加载状态。
即时消息标识技术专题(2):微信群发即时消息序列号生成实践(灾难恢复方案)_2.jpg
▲图2:分段偏移示意图。通过更新加载配置,将0~2个段从AllocSvrA迁移到AllocSvrB
同时,为了防止丢失的AllocSvr提供错误的服务和返回脏数据,AllocSvr需要与StoreSvr保持租用关系。
这种租赁机制由以下两个条件组成:
1)租约到期:当AllocSvr无法在n秒内从StoreSvr读取加载配置时,allocsvr停止其服务;
2)租约生效:读取新的加载配置后,AllocSvr立即卸载待卸载的号码段,待加载的新号码段等待N秒钟提供服务。
即时消息标识技术专题(2):微信群发即时消息序列号生成实践(灾难恢复方案)_3.jpg
▲图3:租赁机制。AllocSvrB严格保证在AllocSvrA停止服务后提供服务
这两个条件确保了在切换时,新的AllocSvr在旧的AllocSvr离线后一定会开始提供服务。然而,这种租赁机制也将在移交的号码段中造成短时间的不可用。但是,由于微信后台逻辑层的重试机制和异步重试队列,短时间的不可用对用户来说是不可察觉的,租赁失败和切换是小概率事件,一般可以接受。
这是AllocSvr容灾切换的基本原则。接下来,我们将介绍整个seqsvr容灾体系结构的演变。
5.容灾1.0体系结构:主动备用容灾
seqsvr的初始版本采用了主机+冷备份机的容灾模式:将整个流体空间平均分成N个部分,几个连续的部分组成一个集合,每个集合有一个主机、一个备用主机和两个允许主机。在正常情况下,只有主机提供服务;当主机出现故障时,仲裁服务在活动和备用之间切换,原始主机离线并变为备用,原始备用主机变为主机并加载uid编号段以提供服务。
即时消息标识技术主题(2):微信群发即时消息序列号生成实践(灾难恢复方案)_1.jpg
▲图4:容灾1.0架构:主动备用容灾
一些学生已经想到了这种容灾建筑。“一主机一备”的模式设计简单,具有良好的可用性——毕竟,两台机器同时不可用的概率极低,我相信许多后台系统都采用了类似的灾难恢复策略。
5.1设计权衡
主动备用灾难恢复存在一些明显的缺陷,如有一半的空闲机是由空闲的备用机造成的;例如,当主机和备用机切换时,备用机将在瞬间接受来自主机的所有请求,这将很容易导致备用机过载。既然一主一备的灾难恢复存在这样的问题,我们为什么要在一开始就采用这种灾难恢复模式呢?事实上,建筑的选择往往与当时的背景有关。seqsvr诞生于微信发展的早期,也是微信迅速扩张的时期。
选择主灾难恢复模式的原因如下:
1)体系结构简单,开发速度快;
2)机器数量少,机器冗余不是主要问题;
3)客户端很容易更新AllocSvr的路由状态。
前两点很容易理解。人力和机器不如时间宝贵。第三点更有趣。让我们在下面讨论一下:
微信后台的大部分模块都使用了自己开发的RPC框架,seqsvr也不例外。在这个RPC框架中,调用者读取本地机器的客户端配置文件,并决定调用哪个服务器。这种模型对于无状态服务器非常有用,对于实现容灾也非常方便。我们可以在客户端配置文件中写下“对于段X,您可以访问SvrA、SvrB和SvrC中的任何一个”,以实现三台主机的容灾。
在seqsvr中,AllocSvr是一个预先分配的中间层,它不是无状态的。正如我们前面提到的,哪些uid段允许加载由存储在StoreSvr中的加载配置决定。此时,令人尴尬。该公司想申请一系列特定的uid。事实上,客户端不知道要访问哪个AllocSvr。客户端配置文件只会告诉它“AllocSvrA,AllocSvrB…这些机器中的一个将具有您想要的序列”。换句话说,最初负责提供服务的AllocSvrA失败了,仲裁服务决定由AllocSvrC代替AllocSvrA提供服务。客户端应该如何知道该路由信息的变化?
此时,如果我们的AllocSvr采用主动备用灾难恢复模式,事情会变得简单得多。我们可以在客户端配置文件中写:对于某个uid编号段,要么允许加载或允许加载。当客户端发起请求时,尽管客户端不知道AllocSvrA和AllocSvrB中的哪一个实际上已经加载了目标uid编号段,但是客户端可以首先尝试向AllocSvr中的任何一个发送请求。即使这次请求了错误的AllocSvr,也知道另一个是正确的AllocSvr,然后可以再次启动请求。
也就是说,对于主动灾难恢复模型,最多只浪费一个测试请求来确定AllocSvr的服务状态,额外消耗较少,编码简单。但是,如果Svr采用其他复杂的灾难恢复策略,基于静态配置的框架很难确定Svr的服务状态:Svr的状态会发生变化,客户端无法确定将请求发送到哪个Svr。这是最初选择主灾难恢复的原因之一。
5.2主备灾难恢复的缺点
在我们的实际操作中,容灾1.0体系结构有两个主要缺点:
1)膨胀和收缩非常麻烦;
2)一台设备的主备机过载,其他设备不能用于灾难恢复。
在主灾难恢复中,客户端和AllocSvr需要使用相同的配置文件。当更改此配置文件时,它不能同时更新到所有客户端和AllocSvr,因此需要非常复杂的手动操作来确保更改的正确性(包括需要使用iptables进行请求转发,此处不再详述)。
对于第二个问题,常见的方法是用一致的哈希算法替换活动的和备用的。一套有多台机器,过载机器的请求被分配到多台机器上,灾难恢复效果会更好。在seqsvr中使用类似于一致哈希的灾难恢复策略也是可行的,只要客户端和仲裁服务都使用相同的一致哈希算法,这样客户端就可以试探性地尝试,直到找到正确的AllocSvr。
例如,对于某个uid,仲裁服务将优先将其分配给AllocSvrA,如果AllocSvrA挂起,它将被分配给AllocSvrB,然后它将不会被分配给AllocSvrC。然后,在访问AllocSvr时,客户端按照allocsvra-> allocsvrb-> allocsvrc的顺序进行访问,也可以达到容灾的目的。然而,这种方法仍然没有克服灾难恢复所面临的配置文件改变的问题,并且操作起来也非常麻烦。
6.容灾2.0架构:嵌入式路由表容灾
6.1基本原则
最后,我们采取了不同的方法,采用了不同的思路:由于客户端和AllocSvr之间存在路由状态不一致的问题,AllocSvr可以将当前的路由状态传递给客户端,打破了以前只能根据本地客户端配置文件进行路由决策的限制,从根本上解决了这个问题。
因此,在2.0架构中,我们将AllocSvr的路由状态嵌入到客户端请求序列的响应包中,在不消耗额外资源的情况下实现客户端和AllocSvr之间的一致路由状态。具体实施方案如下:
seqsvr的所有模块都使用统一的路由表,该表描述了从uid编号段到AllocSvr的完整映射。该路由表由仲裁服务根据AllocSvr的服务状态生成,写入StoreSvr,作为租约由AllocSvr读取,并最终在服务返回数据包中绕过客户端。
即时消息标识技术专题(2):微信群发即时消息序列号生成实践(灾难恢复方案)_2.jpg
▲图5:容灾2.0架构:动态段迁移容灾
将路由表嵌入到请求响应包中似乎是一个简单的体系结构变化,但它是整个seqsvr灾难恢复体系结构的一个技术奇点。解决了路由状态不一致的问题后,可以实现一些以前不容易实现的功能。例如,灵活的灾难恢复策略使所有机器相互袖手旁观,当机器发生故障时,故障机器上的数字段被平均迁移到其他可用的AllocSvr还可以根据AllocSvr的负载情况进行负载均衡,有效缓解AllocSvr请求不均衡的问题,大大提高机器利用率。
此外,操作已大大简化。以前,机器的操作和维护都有复杂的操作步骤,但新的体系结构只需更新路由就可以轻松实现在线、离线和机器更换,不需要关心配置文件的不一致性,从而避免了人工误操作造成的一些故障。
即时消息标识技术专题(2):微信群发即时消息序列号生成实践(灾难恢复方案)_3.jpg
▲图6:机器故障号段的迁移
6.2路由同步优化
将路由表嵌入获取序列的请求响应包将引入一个哲学命题,如“先有鸡还是先有蛋”:如果没有路由表,您如何知道从哪个AllocSvr获取路由表?另外,取序列是一个超高频的请求,如何避免嵌入路由表造成的带宽消耗?
这里,路由表和路由版本号缓存在客户端。请求步骤如下:
1)客户端选择相应的AllocSvr根据缓存在本地共享内存中的路由表,选择合适的路由;如果路由表不存在,随机选择一个AllocSvr;
2)向所选择的AllocSvr发起请求,并携带本地路由表的版本号;
3) allocsvr收到请求后,除了处理顺序逻辑外,还判断客户端上的版本号是否是最新的,如果是旧版本,则将最新的路由表附加到响应包中;
4)当4)客户端收到响应包时,除了处理顺序逻辑外,还判断响应包是否有新的路由表。如果是,请更新本地路由表,并决定是否返回步骤1重试。
基于上述请求步骤,当本地路由表失败时,可以通过几次重试来提取正确的路由,并且可以正常提供服务。
7.全文摘要
经过两次解释,seqsvr的技术原理、架构设计和实际架构演进基本完成。正是这种简单优雅的模式为微信的其他模块提供了简单可靠的一致性解决方案,支持了微信在过去五年的快速发展,相信在可预见的未来仍将发挥重要作用。
----------------------------------------------------------------------------------
哇谷im_im即时通讯_私有云_公有云-哇谷云科技官网-JM沟通
IM下载体验 - 哇谷IM-企业云办公IM即时聊天社交系统-JM 沟通下载
IM功能与价格 - 哇谷IM-提供即时通讯IM开发-APP搭建私有化-公有云-私有化云-海外云搭建
新闻动态 - 哇谷IM-即时通讯热门动态博客聊天JM沟通APP
关于哇谷-哇谷IM-提供企业即时通讯IM开发-语音通话-APP搭建私有化-公有云-私有化云-海外云搭建
联系我们 - 哇谷IM-即时通讯IM私有化搭建提供接口与SDK及哇谷云服务
公有云和私有云之间有什么区别?类似融云、环信云、网易云、哇谷云?