作者丨张黎明:腾讯高级工程师,负责 SNG 接入层、逻辑层和公共组件的运营维护。参与了大规模公共组件推广运营、社交平台的异地容灾分布、自动化运维系统建设等项目。
腾讯内部一些基础服务比如统一鉴权登录、社交关系链、支付被内部很多其他业务调用,调用方往往横跨几个事业群,几十个部门,有数百个模块,上万台设备。
腾讯工程师小明就是这种基础服务的运维负责人,在推动织云路由作为统一寻址服务之前,调用方各显神通,有 IP 直连访问的,有 DNS 解析访问的,也有通过自己的寻址服务访问的。
一段时间下来,小明发现有点吃不消了,所负责的服务一旦有设备故障,调用方一堆人找过来咨询和投诉,而小明作为服务提供方没有快速剔除故障设备的方案,只能通知调用方各自去解决。
在碰到裁撤迁移或者扩容这种场景,小明又很头痛,几百个调用方,需要逐个去通知变更设备列表,需要花上一两周甚至一个月的时间才能把流量切换完毕。一旦有调用方通知不到位或者修改不到位,还会造成服务提供方的负载不均,影响服务质量和运营成本。
小明痛定思痛,下定决心推动各个调用方统一通过织云路由进行寻址调用,改造之后,单机故障自动屏蔽、流量妙极切换且对调用方完全透明,同时在织云路由就近访问的基础上对服务进行了多地分布,做到了异地自动容灾切换。类似于小明这样的典型案例还有很多。如果你所在的运维团队正面临如下挑战:
那花 10 分钟时间了解一下织云路由或许能有所收获,织云路由是应用层实现的一套兼具负载均衡和自动容错的名字服务系统,能有效地解决上述问题,已在腾讯多年海量服务的支撑过程中,根据业务实际需求和碰到的问题不断改进优化,在负载均衡和名字服务上沉淀了很多别具匠心的设计。
由于其简洁,易用,实用的设计,语言丰富的 API ( C/C++,PHP,Java,Python,Node.js 版本 API 可供使用),系统的高可靠性,高可用性,迅速被业务广泛接受,目前已在腾讯内部众多业务中广泛应用,支撑了 QQ、Qzone、移动支付、社交广告等核心业务,覆盖近 20 万台机器,1 万多个业务模块,5 万多个注册名字,90 多万个被调节点。接下来给大家讲解一下织云路由的工作原理和独到之处。
下图描述了一个典型的互联网服务三层架构,以及织云路由在整个架构中的位置和作用。
由上图可知,对于任何层次需要进行网络远程调用的节点,都可以使用织云路由带来的负载均衡和容错服务,典型的如:接入层服务器访问逻辑层服务器,或者逻辑层服务器访问存储层服务器(比如这里,如果接入层机器为主调机器,那么逻辑层机器为被调机器;如果逻辑层为主调机器,那么存储层就为被调机器)。
再结合下图织云路由架构图来进行更详细的了解:
提供 web 页面和管理系统接口,web 页面用于名字到 ip:port 映射的增删改查操作;管理系统接口提供给上层的调度系统,可以进一步结合织云的容量、发布系统实现服务的自动伸缩调度。
路由 server 用于读取路由配置 DB,然后和织云路由的客户端 agent 进行通信,将最新的配置信息发送给 agent。路由 server 是无状态的,且能够多台平行拓展,通过架构冗余实现高可用性。
业务进程需要通过 API 的两个接口函数 GetRoute 获取到访问后端的路由,访问完成之后调用 RouteResultUpdate 接口函数对访问结果进行上报。这期间业务进程通过 API 与 agent 进行通信,agent 根据上周期的统计结果进行动态计算,对 GetRoute 返回的路由提供决策支持。
从上可以看出,织云路由本质上是一个路由决策系统,不包含远程过程调用实现与业务协议实现,跟业务系统基本没有耦合,也不参与到请求的实际处理数据流中,不作为业务系统的关键路径。另外,系统本身从 API 到客户端 agent 都有缓存,所以即使整个路由系统挂了,短时间内也不会对业务有影响。
与传统的负载均衡解决方案 LVS 和 F5 相比,很明显,织云路由具有不增加网络延迟,不会成为网络瓶颈,不需采购专门硬件,部署快速简单,路由策略易于实现,扩展等特点。唯一需要是在业务代码中增加对路由 API 进行一些必要的调用。
提供以 ID (由模块 ID 和命令字 ID 组成)获取 IP 和端口地址的方式,使得 IP 和端口配置对调用者透明;变更全网秒级生效;
织云路由的负载均衡工作原理可以抽象为:基于机器初始配置信息,通过自适应算法,以两个关键反馈指标请求成功率和延时为依据,周期性计算出每个被调机器的权重,再使用高效的配额算法分配下个周期各个被调机器的请求数;这样通过不停的动态迭代,使得被调服务保持高效,健康的状态,做到能者多劳,物尽其用;
Agent 计算各被调节点权重 W(k)的算法描述如下:
服务器 k 的最终权重 W(k) = 动态权重 Wd(k) * 静态权重 Ws(k)
服务器 k 的动态权重 Wd(k) = 1/load(k)
服务器 k 的负载值 load(k) = delay_load(k) 延时倍率 + ok_load(k) 成功率倍率
服务器 k 的延时负载值 delay_load(k) = delay(k) / min(delay(0) .. delay(n))
服务器 k 的成功率负载值 ok_load(k) = max(ok_rate(0) .. ok_rate(n)) / ok_rate(k)
从上面的公式我们可以看出动态权重 Wd(k)与被调节点的成功率 ok_load(k)正相关,与时延 delay_load(k)反相关。静态权重可用于手动调节节点权重,用于灰度,压测等。
支持应用层接口级自定义容错,业务可根据不同场景将后端服务需要容错的情况用 RouteResultUpdate 接口把访问结果上报为访问失败即可。由调用者上报实际调用结果,使得统计更真实,容错更精准,避免一般拨测系统在网络分区情况下容错不当的问题,如下图:
有时服务提供方的 A 链路出现问题,并不代表服务调用方到提供方的 B 链路也有问题。
此外,agent 根据上报,在统计到后端服务某个节点失败率过高,会对该节点的请求量进行快速收敛,超过特定阈值,会认为有宕机嫌疑,暂时屏蔽掉;然后进入对这台设备的宕机恢复检测阶段。在宕机恢复检测阶段,agent 会先进行网络层探测,探测节点的联通性,在节点连通性探测通过之后,会再分配业务真实请求,然后根据请求的成功率情况恢复对该节点的访问,整个容错和恢复过程无需人为干预。
这对于服务的故障自愈和运维效率有极其重要的作用:对于设备偶发性的宕机,运维人员可以制定统一的策略:首先进行重启恢复,如果重启无法恢复的故障设备,在判断宕机不会带来高负载的情况下,在特定时间批量处理故障设备,无需运维人员实时响应。
以腾讯的用户量和社交服务的特性,出现服务过载的情况很常见,过载保护也是每个业务后台开发的必修课,在公司内部有很多后台服务编程框架有过载保护的功能设计,这些方案在业务实践过程中也取得了很好的效果,但这些过载保护的设计都是从服务提供方的角度去考虑的,织云路由作为嵌入在业务调用方的一个公共组件,实现过载保护对于研发效率来说有事半功倍的复用效果。
织云路由会在后端服务失败率超过阈值(可配置)的情况下,快速缩减请求数(表现为 GetRoute 函数返回-10000,当前周期部分请求无法获取后端服务的 IP:port ),以防止后端服务雪崩。在服务成功率恢复的情况下,过载保护状态会自动消失。
织云路由过载保护的逻辑是:在后端服务已经达到处理极限的情况下,前端调用方尽量只给后端服务能承载的请求量,多余的请求只会使后端服务的响应能力恶化,所以在服务调用方就直接把请求拒绝调是防止整个系统雪崩的有效手段。
为了满足业务根据用户 ID 按照号段存储用户数据的使用场景,特定 ID 的数据只能根据该 ID 寻址到特定的服务器,织云路由提供有状态寻址功能:根据据业务提供的 key (这里一般是用户的 ID )和配置的规则,把相同 key 请求路由到固定的一组或一台后端服务上。目前用的比较多的是按一致性 hash 策略寻址。
织云路由结合自身 CMDB 的服务器地域信息,支持就近访问的功能;对于多地部署的业务,能够简化业务配置和管理成本,同时支持自动异地容灾切换(其中一地发生故障,能快速自动将访问量切换到其他地域)
织云路由作为一个通用的负载均衡寻址解决方案,我们跟业界一些通用的解决方案进行了简单的对比:
当前业务程序使用织云路由需要嵌入 API 代码,虽然相比使用支持路由寻址的 RPC 框架,代码改造量非常小,但在很多代理运营第三方程序的情况下,并不具备对程序的修改能力和权限的场景,无侵入为调用程序提供寻址和负载均衡功能将是我们近期工作的重点,未来我们也会针对不同场景,提供更多的使用方式。
目前织云路由已支持私有化交付,可以方便的对接客户的 CMDB 和 OA 认证系统,也可以和织云一整套自动化运维解决方案打包交付,有意向的客户请联系 [email protected] 。