作者| 谢志旺,字节跳动业务架构 - 平台标准与解决方案
1. 什么是单元化
单元化的核心理念是将业务按照某种维度划分成一个个单元,理想情况下每个单元内部都是完成所有业务操作的自包含集合,能独立处理业务流程,各个单元均有其中一部分数据,所有单元的数据组合起来是完整的数据(各企业实际落地过程中会结合实际业务和基建情况做一些折中)。流量按照某种分区维度(例如流量所属用户)Sharding 到不同的单元,调度上按照流量携带的分区信息进行调度,保证同一时刻该分区的数据写入都在同一个单元,简化版示意图如下:
2. 为什么要做单元化
业界各企业演进到单元化一般主要都是出于下面几个原因:
除了上述最关键的问题外,建设单元化还能有其他方面的收益:
3. 异地单元化的主要挑战
机房延迟问题 :一般同城机房之间物理距离 <200KM,网络延迟和机房内差别不大,大部分业务场景无需过多考虑。但跨城异地机房之间的延迟会大很多,例如北京和上海之间 RTT P99 达 40 毫秒,此时跨机房请求耗时需要慎重考虑,特别是单个请求如果出现多次跨机房,增加的请求耗时可能是几百毫秒甚至更大的。
数据同步问题 :
流量路由的问题 :所谓流量路由也就是如何将流量调度到正确的单元问题,需要考虑在请求链路哪个环节(客户端、流量入口、内网 RPC、存储层等)、根据请求什么信息(用户 ID、地理位置等)进行用户归属单元的识别,以及如何进行走错单元流量的纠偏。
数据正确性问题 :
成本问题 :
管理复杂度问题 :随着单元的增加,不同的单元都需要管理对应的服务和基建,管理和运维的复杂性会有较大增加。
4. 字节跳动异地单元化架构
在本文中,我们仅对字节跳动在 中国大陆 的单元化架构做讨论,目前我们的单元化部署架构如下图所示:
我们围绕客户端选路、接入层纠偏、计算层纠偏、存储访问层管控四个维度构建了单元化流量调度和管控能力,通过技术手段确保单元化流量调度的正确性(将流量调度到正确的单元)和数据访问的正确性(数据不写脏),具体来说:
目前包括抖音、抖音电商、抖音支付、抖音直播、抖音本地生活等业务均启动了异地单元化改造落地,生产环境已接入数千个核心微服务、超过 100 万实例。
5. 单元化架构落地的关键问题
5.1 如何选择单元的维度
单元维度的选择很大程度上决定了单元化架构的水平扩展能力、运维成本和容灾方式,需要结合业务推进单元化架构想要解决的核心问题(资源、合规、容灾)和业务特性来选型。业界实践大部分是围绕物理维度(例如 Region、机房)构建单元,也有少部分是逻辑维度的。
结合我们面临的业务特性:
综合考虑,我们认为字节跳动的单元应该是物理维度的,单元的建设随着基建的规划和建设去演进,而不是业务自行构建,这种物理意义上的唯一调度依据能够保证不同的业务长期演进中能够在一个单元内闭环,避免调度混乱。
同时考虑我们目前最核心要解决的问题是资源和容灾问题,因此我们最终选择是 Region 维度构建单元,形成当前的
同城容灾+异地多活
容灾架构。
5.2 如何选择分区维度
分区维度决定了数据和流量在单元间的划分标准,选择分区维度的时候需要重点考虑:
一般 ToC 类业务最常见的就是以用户作为分区维度,我们目前大部分业务选择用户(UserID)作为分区维度(抖音、电商等业务),少部分选择 Region 维度(搜索等业务)。
5.3 如何进行流量的单元化调度
5.3.1管理分区和单元的映射
分区和单元的映射也就是对于一个请求我们找到它应该调度到哪个单元的依据,需要综合业务上对管理成本和灵活性的要求来考虑,我们通过以下两种方式结合来管理:
5.3.2计算路由信息和纠偏
路由信息的计算逻辑一般不复杂,更关键的是决策需要在哪些环节计算以及怎么纠偏(把走错单元的流量调度到正确单元),一般来说,整个请求链路包括客户端、接入层、计算层和存储层四层,需要结合公司基建情况和业务要求来决策落地方式,以下是我们对于各层实现纠偏能力的必要性和实现方式上的思考和建议。
客户端:
接入层(负载均衡、网关) :
计算层 :
存储层 :
一个比较完整的分层示例:
5.4如何适配复杂的业务调度场景
理想的单元化架构是所有服务都能在单元内闭环,不出现跨单元的流量,但是在实际业务场景下很难满足理想的单元化架构。以电商场景为例,如果用买家作为分区维度,那商家的数据必然会被多单元读写,因此一定存在部分服务的流量不能单元化调度的情况,单元化架构需要能做好适配。和业界基本思路类似,我们对服务类型做了区分:
服务类型决定了流量调度方式和服务访问的数据的同步方式。基于此,研发核心需要关注业务上服务类型的定义即可,其他包括数据同步管理、流量调度的细节都可以由框架/平台内部收敛解决,极大降低业务上的理解和管理复杂度:
5.5如何进行多单元数据管理
5.5.1单元间数据同步
单元化架构下的数据同步有两种场景:
5.5.2数据同步一致性比对
数据对账一般是全增结合,实时增量比对确保 Diff 发现的实时性,但写入 TPS 高时会有误差,周期全量比对兜底保证整体一致率,常见的一致性比对流程如下:
设计增量数据一致性比对能力的时候,需要重点考虑对热键的检测和处理,部分热键一直在 Update 的话需要能识别出来,否则会导致持续有不一致误报警,一个简单的基于重试比对的实现如下:
5.6如何保证数据多活的正确性
5.6.1日常态:异常单元化流量的识别和拦截
以 UserID 作为单元化分区维度的话,在实际业务场景可能出现两种异常情况:
这两种情况我们都认为是异常单元化流量,在存储访问层通过管控中间件支持了识别和拦截能力,兜底保障数据不被写脏:
5.6.2切流态:避免数据同步延迟导致脏写
在做单元间切流的时候,由于下面两个问题,可能导致数据出现脏写:
我们对切流过程引入
两阶段配置变更+存储访问禁写
来解决上述数据脏写问题,整体切流流程如下:
得益于我们整体单元化调度和存储访问中间件设计,我们的禁写是 UserID 维度的,能够控制到每次切流仅禁写切流中的用户,将切流的影响做的尽可能小。
5.7如何确保切流的可靠性和风险控制
5.7.1优化配置下发时效
结合前面关于切流期间通过
两阶段配置变更+存储访问禁写
来避免数据出现脏写的说明,可以看到配置下发生效的时间对于禁写时长是一个非常大的影响因素,而禁写生效时长直接影响切流那部分用户的使用体验,因此我们需要尽可能提升配置下发时效。
目前字节接入单元化的 Pod 数已超过 100 万,在这么大规模的实例数下快速下发调度配置的挑战是非常大的,我们通过
长连接主动推送+定时轮询拉取
的方式进行配置分发,提高配置收敛速度,减少配置不一致中间态的时间:
5.7.2防止路由死循环
配置发布过程中,同一个 PSM 不同单元实例的生效时间不一样,可能导致流量纠偏到目标单元后由于目标单元重新计算纠偏回来,出现路由死循环的情况。我们通过单元化调度组件在流量上下文中记录并传递纠偏次数,拦截重复纠偏的流量,及时阻断回环流量:
5.7.3切流过程业务架构各层状态检查
5.8如何保证跨地区 RPC 质量
单元之间通常物理距离较远,网络专线建设难度大成本高,网络质量(延迟、丢包、可用带宽)也差于同城机房间。前面介绍的各种单元路由纠偏都会产生跨单元 RPC,相比单元内 RPC 其成功率/耗时指标有所劣化。为了提升跨单元 RPC 质量,除了网络专线本身的稳定性建设(属于网络基建范畴,此处不展开),也可以在架构设计和带宽使用策略上进行针对性的优化。
跨单元 RPC 通道收敛 :在单元化流量路由场景,某个流量很大的 RPC 服务可能仅小比例命中跨单元纠偏,此时由于下游实例数量很多可能导致连接复用效果较差。此时如果让所有跨单元 RPC 先统一经过本地边界网关,然后传输到其他单元的边界网关,最后再转发给实际下游,则建连过程拆分成了 3 段,中间段(网关之间)被所有跨单元 RPC 共用,可以保持较高的链接复用率; 另外 2 段为本地建连,即使连接复用率低,带来的耗时增加也较少。此外收敛到网关通道也更容易集中对跨单元 RPC 进行其他优化,比如按接口等级进行 QoS 管控、在专线完全故障情况下对重要的控制信令做加密后 Fallback 到公网传输等:
跨单元网络分级 QoS 管控 :长距离专线建设成本高,带宽有限,也更容易因为各类异常导致可用带宽变少,无法满足所有场景的跨单元网络传输的需求,因此需要对不同类型的跨单元流量进行分级 QoS 管控。跨机房 RPC 失败要么直接影响用户体验,要么导致故障无法操作止损,应在跨单元网络传输中给予更高的优先级; 离线数据传输容易短时间大量占用带宽,异常对用户感知相对不明显,应给以较低优先级,严格限制上限,必要时还应让出带宽:
6. 未来演进思考
6.1多单元研发成本和效率优化
字节跳动从原本的单 Region 内同城容灾架构演进到多 Region 异地单元化架构周期比较短(一年半左右),基础设施对多 Region 视角的支持还比较不足,对业务的整体研发和业务管理成本偏高,需要将多 Region 的研发和业务管理成本打平到单 Region。
6.2极致的成本优化
从计算资源成本视角:在原来三机房同城容灾模式下,每个机房需要预留 50% 的 Buffer 用于机房故障容灾,演进到异地单元化架构后,基于两个容灾单元间的六个机房,部分业务机房故障可以将流量分摊到其他五个机房,此时各机房仅需 20% 的 Buffer。
从存储资源成本视角:我们现在是
同城容灾+异地多活
的容灾模式,各单元都支持同城容灾,因此部分业务可以直接进行数据的单元化拆分,单元内各自只有一部分数据(加起来是全量数据),理想情况下存储成本减少一半。
6.3更复杂的单元化架构演进
未来字节跳动在国内会有更多的区域,不同业务在各单元的排布模型会越来越复杂,结合我们复杂的业务依赖关系,这里的流量调度模型、数据单元化和同步模型都需要演进。
未来区域增多后,业务随着发展机房排布会调整,可能会需要在非容灾单元之间调整流量,此时存在数据单元化拆分和用户维度数据单元间搬迁能力,需要解决用户维度数据的识别和低成本搬迁问题。
6.4更完善的数据多活能力
字节跳动目前的存储对 AP 场景更友好(侧重抖音这种社交类场景),主要围绕单 Region 构建,在多单元场景下对于电商、支付类(对数据一致要求非常高)的业务支持较弱,在异地单元化架构下强依赖数据同步能力来支持多单元数据多活能力,业务上的限制偏大(例如写只能统一在一个单元),有跨 Region 强一致数据库的需求。