多活架构设计之路由服务设计

347次阅读  |  发布于2年以前

一 、背景

随着公司的业务发展,每次稳定性故障带来的影响越来越大,提供稳定的服务,保证系统的高可用已经变成了整个技术部面对的问题。基于这种背景,公司开展了多云/多活的技术项目,本人有幸参与了 “次日达” 项目【1】的异地双活改造方案的设计。想以此来浅谈一下我对多活乃至全球化的一些技术方案的认知。

多活架构系列的文章我会按照总体技术方案、双活/全球区域化部署技术、网络调度技术、性能优化以及SRE五大部分来展开。本篇毒家Blog会着重讨论总体技术方案以及双活/全球区域化部署技术中的路由服务设计模块,并会在后续的毒家Blog中逐步完善多活架构的完整技术方案。

二、多活/全球化的技术要求

网络服务除了要满足用户对性能、可用性等的基础要求外,多活/全球化的背景下还增加了合规、数据隔离性等要求。而这方方面面的要求都遇到了全新的挑战。

1. 性能

用户从发起请求到接收、响应的时效越短,代表性能越好。但是在双活/全球化的背景下,用户可能在日本,机房可能在中国,物理的距离变得更长了,对应服务的响应时效也会成正比。有测试数据显示,跨国家或者大范围的跨机房调用,网络的RTT会增加1s左右,而这1s可能会造成交易达成的“转化率”下降,甚至用户流失。

2. 可用性

多活\全球化的业务会跨越时区,这就要求我们的服务要7✖️24小时可用。这不仅是对系统的挑战,也是对人力的挑战。

3. 互联互通

互通互联指的是电信网络之间的物理连接。为了使一个电信运营商企业的用户可以和另一个电信运营商企业的用户进行相互通讯,在国内,这种跨越运营商的网络通讯已经不是什么大的问题。但是在国外,很多国家的网络互通互联的质量仍然不够理想。

4. 数据一致性

当数据被全球用户共享时,多地用户都可进行读写操作,如何确保数据的一致性?

5. 隐私保护

全球化的业务必须遵守GDRP(General Data Protection Regulation,通用数据保护条例)。

6. 可伸缩性

维基百科的解释:当系统、网络或者进程在任务量增减时,有能力进行应对。

三、多活/全球化的总体架构

3.1 领域建模

系统的核心就是处理领域模型的关系,从领域模型出发让整个系统满足现状和未来的需求。同时让项目团队更好地协作,以下是系统的核心对象。

User:网站平台上的用户。

UserGroup:用户组、具有相同特征的用户一般会采用同样的网络链路和机房调度策略,因此会被归属到同一个用户组之中。实际上,多活系统是以用户组为基础调度单位的。用户是用户组的一员,我们既可以以用户组为单位调度,也可以针对某个具体用户调度。

EdgeNode:边缘节点。可以理解为静态资源的提供节点。比如图片、js文件、css等。一般的边缘节点指的是CDN。

NetLink:网络链路。

NetNode:网络节点。

IDC:机房。

DNS:DNS服务器。

HTTP-DNS:理解成APP端的DNS服务器。

DomainName:域名。

VIP:虚拟「偶像」。

以上领域模型的关系如下;

3.2 总体架构

按照以上的描述,其实是可以看到,从用户出发一直到机房,会存在边缘节点调度、网络调度以及机房调度。还有调度的执行(路由的使用)以及调度的控制(路由的产生)。路由即每个用户或者每个用户组隶属于那个机房。结构图如下;

四、多活/全球化区域化部署技术

4.1 整体架构

4.1.1 功能优先级

调度的编排具体策略由业务需求决定,通常来讲就是会考虑合规、数据一致性、可伸缩性、成本、容量、性能和稳定性来考虑。通常重要顺序是

合规 > 数据一致性 > 可伸缩性 > 其他

4.1.2 部署架构

目前我们有四个机房(A洲、B洲、C洲、D洲),建设每个机房所在的地域服务对应的地域用户。机房之间的数据需要按需复制,每个机房都部署所有的应用以及数据库,使得每个机房都是对等的。当备份好数据之后,就可以让所有的机房互为灾备机房。数据一致性和可伸缩性会在后续介绍。

4.1.3 问题分析

以电商场景中的买卖、就近访问以及异地容灾为例。比如买家和卖家分别来自不同的地区,进行交易必定会有部分共享的数据一致性问题;当异地容灾时,也会面临数据一致性问题;当用户迁移到其他区域时,仍然要保证就近访问,那么又涉及到同一个用户的数据一致性问题。

我们应对的策略如下:

4.1.4 解决方案

从应用程序分层的视角来看,解决方案如图;

4.2 路由服务

区域化部署技术本质就是多层路由,而在每一层路由中,都是基于用户对应的归属机房调用的路由的。路由服务的作用就是告诉调用方,这个用户归属于哪个用户。

  1. 路由服务结构

a . 内存路由表:理解为 HashMap,key 为用户 id,value 为用户归属机房以及用户状态。

b . RPC 服务。

  1. 路由表如何使用

a . 用户请求进入机房第一个应用程序是同一接入层。使用 Nginx 作为统一接入的应用程序,Nginx 内嵌路由表,并且在多进程进行共享。Nginx 接受请求后做的第一件事情就是获取用户 id,然后调用路由表取得用户归属机房以及用户状态。若用户归属于本机房则继续向下透传。

b . 下游需要路由信息直接获取上层丢下来的路由信息。如下图;

路由 透传 存在时效限制,当超出一定时效,透传内容会失效。至于在透传过程中,用户路由改变怎么办?本文后续解答。

4.2.1 路由表原理

路由表设计规范必须了解以下几点:

4.2.1.1 方案比较

方案比较包括以下的引入分布式缓存、HashMap、布隆过滤器等,以下的方案各有缺点,具体如下。

4.2.1.1.1引入分布式缓存
4.2.1.1.2 HashMap
4.2.1.1.3 布隆过滤器

这样看来没有一个现存的方案,需要根据场景来**定制**化路由表。

4.2.1.2 路由表设计

基于上述启发,选择使用比特数组进行存储路由信息。我们可以用4个 bit 来表达一个用户。如图所示;

这样存储的话存储一亿的数据只需要47M左右的内存空间。

但是如果用户ID的分布是分段的呢:

0~ 80000000

100000000~ 300000000

700000000~ 800000000

2000000000~ 2000100000

尽管真实的用户数量也只有1亿左右,但是id分布如此广泛,这样大约要消耗900多M的内存。

基于这点,要引入分段模式:

4.2.1.3 路由表相关设计

上阶段解决了路由表的基础存储方案,但是有一些场景还是需要我们持续设计改进的。现在我们思考两个问题:

具体的配置逻辑可以基于各个公司使用的配置系统来进行集中配置。

4.2.2 路由表更新机制

路由表更新机制的确立需要有以下设计约束;

4.2.2.1 数据一致性思路

很多时候分布式系统都在解决一个问题,那就是如何让任何一条记录修改在所有机房或者多机房上同时生效。解决思路并不复杂,并且有通用性。虽然我们无法保证变更在所有机房或者多机房同时生效,但我们可以知道变更在多机房中是否已经生效,在此基础上我们设置一个中间状态,这个状态与变更前的状态和变更后的状态都兼容,就解决了这个问题。

如图所示,状态A是变更之前的状态,状态C是变更后的目标状态,状态A与状态C是不能同时出现的,但是状态A变更为状态B,在等待直到所有机房的所有相关机器全部都变更为状态B,那么再从B到状态C,这样就不会出现状态A和状态C同时出现的情况。

为了解决路由更新过程中业务数据全局一致性问题,我们引入了一个“禁写”过渡版本。在切换到目标路由机房之前,我们先将路由置为当前机房的“禁写”过渡版本,在这个状态下,用户不能继续在当前机房以及其他任何机房中进行任何会修改相关业务数据的动作。在“禁写”过渡版本变更到新版本之前,必须确保所有路由解析的本地版本已经升级到“禁写”过渡版本。“禁写”过渡版本将新旧路由版本的生效时间严格的隔离开来,不存在某个时刻新旧版本的路由都生效的情况,从而确保了业务数据的全局一致性

(注释:处在“禁写”过渡版本中的用户在“禁写”过程中的其他业务的可用性会受到一定程度的影响。这种影响应当被业务所接受,将其理解为一种业务可用性的局部临时降级。这种降级会安排在用户不活跃的时段,往往不会对用户的体验造成太大的影响。回归到路由表的视线中,用户Id是不需要被存储的,归属机房对应用户bit的前三位,可写标志对应的第4位,为0时表示用户可写,为1时表示用户禁写。)

4.2.2.2 解决方案

4.2.2.2.1 数据准备与生效过程分离

“禁写”状态会对用户产生影响,如果用户被禁写,则意味着用户无法下单,虽然在用户不活跃时段进行变更可以降低对用户产生影响的概率,但是变更可以在此基础上进一步降低这个概率。我们可以采用数据准备与生效过程分离的方式实现。

4.2.2.2.2 一致性具体方案

Zookeeper 是一种高性能的分布式协调工具,用于节点之间的通讯,常被使用分布式的配置管理中,各个厂商在路由表的数据一致性的建设中,大部分使用的也是这种解决方案。

在分布式协调场景中,常常会用到短暂节点,这个节点与创建他的session同在,当session消失,节点也会消失。这个机制常用于做心跳检查。而在路由节点的建设中,所有需要监听路由表的节点都会创建一个短暂节点,用于路由表加载节点的心跳检查。单次变更的流程如下:

上述步骤说明的ZK的目录节点结构如下:

4.2.2.3 整体架构

前面对关键的技术细节进行了介绍,下面介绍整体的架构。前面介绍过,管控系统会负责所有机房的区域化管理,包含路由表的变更流程。在每个机房中都会有一个管控的Agent,管控系统会调用Agent对所有的机房进行管理。在路由变更过程中,对每个Agent会把机房中的路由数据写入对应的分布式数据库中,Zk再推送信息写入,这里不再赘述。

4.2.2.3 变更流程

当路由表变更时,完整的流程变更如下:

1 . 保存当前版本号V1,用于处理回滚的方案。

2 . 获取当前机房列表,得到所有机房,循环调用每个机房的Agent,依次向下执行。

3 . 每个机房的Agent调用我们上述说的解决方案,将数据写入分布式数据库内。

4 . 如果失败,则直接调用第8步。

5 . 获取当前机房列表,得到所有机房,并且循环调用每个机房的Agent,将用户状态修改。

6 . 之后利用一致性的具体方案(4.2.2.2.2),并将所有用户状态改成最终状态。

7 . 如果失败,则直接调用第8步。如果成功,则流程结束。

8 . 循环调用每个机房的Agent,将版本回退。如果失败,则进行人工干预。

4.2.3 用户路由更新方案

前面介绍过了路由表的更新机制,但是如何确定用户归属的机房?如何变更用户归属机房?如何将网站的存量用户加入到路由表中?以及有了新用户如何加入路由表中?

4.2.3.1 确定**用户归属机房**

在真实应用场景中,绝大部分用户的归属逻辑采用性能优先原则,基本等同于用户归属于访问延迟最小的机房。当然对于大部分场景下,延迟最小的机房就是物理距离最近的机房。

我们如何来判断用户的归属机房,方案如下:

1 . 每个用户都会在所有机房进行异步访问,用于确认用户和所有机房的延迟。

2 . 以用户区域为粒度进行统计,最稳定的机房是哪一个。

3 . 在路由表中将区域中每个用户与这个区域整体表现最好的机房做关联。

4.2.3.2 变更用户归属机房

确定了用户归属机房之后,假设新的归属机房与原机房不同,那么就要落实一个用户到机房的归属。前面介绍过,在用户路由归属过程中,需要将表改写成向前向后都兼容的“禁写”状态,这个过程确保了路由表本身变更不会带来数据不一致的情况。但是从“禁写”用户到用户“可写”的过渡中,还需要将用户的数据在原机房复制到目标机房,并且确保复制完成。相关数据复制的技术这里不展开讨论,会在后续章节讲述。

4.2.3.3变更优化-分时变更

由于禁写可能会对用户产生影响,因此我们需要在变更的时间上进行优化,降低对用户生产影响的概率。主要的方法就是找到用户最可能闲时。

(1)以小时为单位,并且赋予时间段标识id。

时段标识id 时段
0 0-1
1 1-2
2 2-3
3 3-4
4 4-5
…… ……
23 23-24

(2)为用户的不同行为设置权重,权重代表禁写对用户影响的大小。

事件 权重
browsing 0.2
ordering 0.8

(3)建设用户 abc 在一段时间内操作记录如下,则采用下面的计算方法计算每个事件的冲突值。

用户id 事件 活跃id=0个数 活跃id=1个数 …… 活跃id=23个数 总个数
abc browsing 1 2 1 4
abc ordering 4 2 2 8

P(0)=1/(1+2+1)*0.2+4/(4+2+2)*0.8=0.45 代表标识id为0的时间段冲突值为0.45

P(1)=2/(1+2+1)*0.2+4/(4+2+2)*0.8=0.3 代表标识id为1的时间段冲突值为0.3

P(2)=1/(1+2+1)*0.2+4/(4+2+2)*0.8=0.25 代表标识id为0的时间段冲突值为0.25

值越大,代表此时间段的避开价值就越大。

4.2.3.4 存量更新方案

存量更新方案是指两种场景

这两种场景一般都是指重新计算所有目前系统中存在的用户的归属机房。基于前面介绍的知识,目前采用的方案就是之前我们提到的确定用户归属机房(4.2.3.1)方案。

这里特殊提一下归属的默认优化,我们将某一个机房作为默认机房,所有归属到此机房的用户无需加入路由表,当调用路由服务查询此用户路由时,路由表返回空值,路由服务直接返回默认机房,从而大大降低路由表的大小。

4.2.3.5 全量更新方案

增量更新方案一般也指的是两个场景

对于第一种情况,新机房的用户都会归属到默认机房,不进行任何路由表的变更,之后的过程与第二种情况相同。

对于第二种情况,在对新用户进行多机房探测过程,发现用户可能不应该属于本机房,或者发现新注册用户确实访问默认机房不是最快的。那么就需要做用户迁移,即进行增量更新。在确认归属之后,增量更新与存量更新方案一致,相比之下,增量更新方案需要变更的用户比较少。存量更新方案需要运行的次数并不多。

五、小结

这一篇文章主要介绍了在异地多活/全球化改造过程中的基本概念以及领域建模还有路由系统的存储优化过程。后续还会持续更新异地多活/全球化的更多内容,欢迎关注「得物技术」公众号。

注释:

【1】次日达(Leadtime、LT)是一款得物推出的履约承诺产品,核心逻辑是通过发货园区、收货城市、商品属性匹配后台配置的线路,以此给用户承诺商品是否支持商品次日送达。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8