突发热点事件下微博高可用注册中心vintage的设计&实践

当前微博服务化采用公有云+私有云的混合云部署方式,承载了每天百亿级的流量,vintage 作为微博微服务的注册中心,为管理 10w 级微服务节点以及在流量激增情况下的服务快速扩缩容,面临了极大挑战。例如:复杂网络条件下 vintage 服务的高可用保障、解决大批量节点状态变更触发的通知风暴、异常情况下 vintage 节点的自适应和自恢复能力、vintage 多云部署场景下节点数据同步和一致性保障等。

vintage 3.0 优雅的解决了上述问题,具备了良好的扩展性,可用性达到 6 个 9。在 12 月 8 日北京 ArchSummit 全球架构师峰会上,来自微博研发中心的边剑,以挑战和问题切入与大家分享了 vintage 设计原则与方案,以及线上应用的最佳实践。

 一、突发热点对微博注册中心的要求与挑战 
1.1 突发热点事件
突发热点事件下微博高可用注册中心vintage的设计&实践

突发热点是微博面临的最常见、最重要的挑战,左图显示的就是最近的一个热点事件,右图是热点发生时的流量图,我们可以看出热点事件发生时,在短时间内会引发微博流量的数倍增长。那么面对如海啸般的突发流量,如何应对就显得尤为重要。

根据峰值流量模型不同,应对策略多种多样,但通常总结为下面三种措施:

  1. 常备充足的 buffer:该方式更佳适用于可提前预测的流量高峰。如:双十一购物节,微博的春晚保障等。而极热事件往往都具备突发性,很难提前预料,并做好充足的准备。同时极热事件发生时的流量往往高于日常峰值的数倍,且不同极热事件之间流量峰值和流量模型不尽相同,因此也很难准确评估 buffer 的数量。

  2. 采用服务降级的方式,保障核心业务:通过对系统接口逻辑中,非核心业务的逐步剪枝,来缓解核心接口整体压力,保障业务接口的功能运行及性能稳定,这类方式在很多年前是比较流行的。但此类方式通常会伴随系统功能受限,页面内容不完整等是用户体验下降的情况发生。此外在极端峰值场景下,还可能会出现降无可降的情况。因此这种方法也不是目前最好的选择。

  3. 弹性扩容: 为了克服前两种措施带来的不足,经过多年应对突发流量的经验积累,微博目前使用混合云的部署方式。通过对后端微服务资源的快速扩容,在短时间内迅速为整个系统构建起坚实的大坝。

1.2 突发热点事件对微博注册中心的要求与挑战

突发热点事件下微博高可用注册中心vintage的设计&实践

众所周知,微服务的运行必须依赖注册中心的支持。这就对微博注册中心提出了 4 点要求:

  1. 要支持每秒百级别以上的微服务扩容

  2. 秒级别的微服务变更通知延迟

  3. 能承载 10w+ 级别微服务节点

  4. 公司级多语言微服务注册 & 服务发现支持

然而即使我们满足了以上四个要求,但时常还会遇到网络抖动,流量极端峰值时专线带宽拥塞等情况。那么如何在网络状态不好的条件下,保障微博注册中心的可用性,实现微服务正常扩容就成为了微博注册中心在设计时必须要面临的挑战。

 二、vintage 高可用设计 & 实践 
2.1 设计目标
突发热点事件下微博高可用注册中心vintage的设计&实践

针对上面提到的要求和挑战,设计微博注册中心时在功能上要实现公司级别的注册中心及配置管理平台,支持多 IDC,支持多语言,具备 10w+ 级微服务承载能力,可用性上需满足 6 个 9。在服务性能方面,要具备每秒对百级别以上微服务的扩容能力。同时要求通知的平均延迟低于 500ms。而在伸缩性上,微博注册中心自身必须具备十秒级节点扩容,以及分钟级 IDC 注册中心搭建。

2.2 选型对比
突发热点事件下微博高可用注册中心vintage的设计&实践

根据设计目标,对业界比较流行的注册中心进行了调研。ZooKeeper,Etcd 无法较好满足微博对多机房支持要求。Consul 虽然满足了多机房的支持,但在网络 QoS 发生时由于其本身的 CP 模型设计,在服务可用性方面不能提供很好的保障。而 Eureka 虽然在多机房和可用性方面都满足微博的要求,但 Eureka 目前仅支持以长轮询的方式订阅服务,在通知延迟方面不能很好的支持,同时 在社区支持方面 Eureka 已暂停了新版本的研发。

综合考虑上述指标 ZooKeeper,Etcd,Consul,Eureka 均都无法很好的满足微博对注册中心提出的要求。因此我们决定在现有的 vintage 系统基础上进行升级改造,来满足具备公司级注册中心的能力。

2.3 vintage 架构设计
突发热点事件下微博高可用注册中心vintage的设计&实践
vintage 部署拓扑

先从系统拓扑部署了解下新版 vintage 系统的整体设计。vintage 服务采用多 IDC 的部署方式,通过 Gossipe 协议,实现集群节点间的自动发现。IDC 间主主通信实现多机房间数据同步。IDC 内采用一主多从的部署方式,基于 Raft 实现了可支持分区多主的选举协议,主从节点角色之间实现读写分离。

vintage 系统采用典型三层结构设计,将网络层,业务层,存储层抽象分离。

  1. 最上层是 Restful 风格实现的 API 接口,提供微服务注册,订阅及健康检测等功能。同时通过 307,308 模块完成对上下行流量的主,从节点分流。

  2. 中间的业务层包括命名和配置两大核心业务的功能实现。在内部功能方面,Node discovery 模块负责集群的全局节点自动发现,timer 和 service state Machine(微服务状态机)共同配合完成对微服务状态的高效管理。同时在 vintage 中的任何注册,注销及微服务元数据更新,节点状态变更等行为都将被抽象为一个独立事件并交由 event handler 进行统一处理。Transfer 和 repair 模块用于整个集群的节点间数据同步及一致性修复功能。而为应对网络问题对注册中心及整个微服务体系可用性产生的影响,vintage 内部通过 partion hanlder 实现网络分区状态的感知并结合多样化的系统保障策略予以应对。

  3. 最下面的存储层:采用基于树形结构的多版本数据存储架构,实现了对众多微服务及其所属关系的有效管理,支持微服务的灰度发布。event notify 模块将数据变更事件实时通知到业务层,配合 watch hub 完成对消息订阅者的实时推送。在数据可靠性方面,采用 aof+snapshot 方式实现本地的数据持久化。

2.4 高可用实践

突发热点事件下微博高可用注册中心vintage的设计&实践

了解整个系统后,将通过网络分区,通知风暴,数据一致性,高可用部署四个方面介绍 vintage 在高可用方面的实践,以及 AP 模型注册中心在数据一致性保障方面的措施。

网络分区

场景一:vintage 内部网络分区

突发热点事件下微博高可用注册中心vintage的设计&实践如上图,网络分区将 vintage 集群一分为二,同时每个分区中的节点数量均少于 n/2+1 个(n 为该 IDC 子集群 vintage 服务的节点总数)。在该场景下基于传统 Raft 协议的注册中心,由于对数据强一致性的要求,将无法提供正常的读写服务。而 Etcd 和 Consul 在解决这个问题时,也仅是通过 stale read 的方式满足了对数据读取的需要,但仍然无法实现在该场景下对微服务注册的需要。

微博注册中心为实现在网络分区下注册中心服务高可用,对原有 Raft 协议选举机制进行改造,实现了可对任意分区情况下的多主选举支持:

  • 解决了分区发生后处于少数分区内 rpc-server 与 rpc-client 的注册及服务发现能力。实现任何粒度分区下 vintage 服务的高可用。

  • 对故障节点的承受能力大幅提升,由原 Raft 协议的 n/2-1 个节点,提升至最大 n-1 节点。

同时在该场景下网络分区后,微服务会将自己的心跳汇报到所属分区的主节点。而由于分区存在,分区间节点数据无法通信。对于(vintage)IDC 集群中的其他主节点来说就出现了微服务心跳丢失的现象,而心跳是作为服务健康状态的重要识别手段。心跳超时的微服务节点将会被标识为不可用状态,从而不会被调用方访问。网络分区时往往会出现大批量的微服务心跳超时,未能妥善处理将直接影响业务系统服务的稳定性及 SLA 指标。而此时 partion handler 模块将会优先于心跳超时感知到网络分区的发生,启动 freeze 保护机制(freeze:注册中心内部对微服务状态的保护机制,基于乐观策略冻结当前 vintage 系统中的微服务节点状态,禁止节点不可用状态的变更,同时对于接受到心跳的微服务节点,支持其恢复可用状态)。

分区恢复后,各节点间通信恢复正常。vintage 会进行再次选举,从分区时的多个主节点中选举出新的主节点。vintage 中的数据变更都会抽象为一个独立事件,系统基于向量时钟实现了对全局事件的顺序控制。在多主合并过程中通过 repair 模块完成节点间数据的一致性修复,保障 IDC 内集群最终一致性。

场景二:client 与 vintage 间网络分区

突发热点事件下微博高可用注册中心vintage的设计&实践

图中绿色和黄色区域分别代表两个不同 IDC 中的 vintage 子集群。红色和紫色代表 IDC1 内的 rpc-server 和 rpc-clinet。此时红色的 rpc-server 与 IDC1 中的 vintage 主节点出现了网络分区,而紫色的 rpc-client 与 IDC1 的 vintage 集群整体存在网络隔离现象。rpc-server 和 rpc-client 之间网络通信正常。虽然这是一个相对复杂且极端的分区场景,但对于微博注册中心的高可用设计要求是必须要解决的问题。下面我们来看看 vintage 是如何解决的。

在该场景下 rpc-server 可通过 DNS 发现的方式获取该 IDC 内 vintage 子集群的全部机器列表,并使用 proxy 模式将注册请求发送至子集群中任意可达的从节点。并通过从节点代理,将注册请求转发至主节点完成服务注册。rpc-server 会定期检测自己与主节点的连接状态,一旦发现连接恢复,将自动关闭 proxy 模式,并通过向主节点发送心跳维护自身状态

rpc-client 可通过跨 IDC 服务发现方式,在 IDC2 中订阅所关注的 rpc-server 服务信息,其自身还会定期将获取到的 rpc-serve 服务列表保存至本地 snapshot 文件。防止即使 rpc-client 与全部的 vintage 服务均出现网络隔离,也可保证自身正常启动及调用。从而实现了 client 与 vintage 服务分区时,微服务注册与服务发现的高可用。

vintage 服务同样支持跨 IDC 的微服务注册。但从实际访问及运维模型考虑,要求每个 rpcserver 节点在同一时刻仅可属于一个 IDC 集群

通知风暴

突发热点事件下微博高可用注册中心vintage的设计&实践

在微博场景下,根据调用方对每种微服务的订阅数量的不同,通常会出现百级至千级别的通知放大。同时老版本 vintage 的服务订阅是基于 sign(md5 后服务列表指纹)对比 + 全量数据拉取的方式,根据微服务自身规模的大小,会出现十级至百级别的消息体放大(二次放大现象)。当出现大量(百级别)微服务同时扩缩容,网络 QoS 或大面积微服务机器故障。经过两次放大后,通知规模可达到百万甚至千万级别,严重占用机器带宽资源。带宽不足导致大量心跳阶段性丢失,微服务的状态极易在‘不可用’和‘正常’之间频繁转换,由此带来更大量的更变事件,出现风暴叠加,导致网络拥塞。更将引发下面两个问题:1. 注册中心业务接口调用失败;2. 大面积微服务心跳汇报失败,被错误标示为不可用状态,影响线上业务微服务的正常调用。

针对通知风暴严重危害,改造后的 vintage 系统使用了“梳”和“保”两种策略应对

突发热点事件下微博高可用注册中心vintage的设计&实践

“疏”主要采用的三种策略

  1. 分流及负载均衡:首先互联网公司通常根据用户的地域和运营商等信息,将请求分流至不同 IDC,并在核心 IDC 内部部署全量的核心服务,保障 IDC 内完成对用户请求的响应。vintage 根据微博流量分配及业务服务多机房部署的特点,在公司几大核心机房均有部署。实现了多机房微服务上行请求的流量分离。同时根据注册中心写少读多的特点在 IDC 内采用一主多从的部署结构,除保障了服务的 HA 外,也对下行流量实现了负载均衡。

  2. 快速扩容:对于通知风暴造成的下行流量,可通过对从节点快速扩容的方式提供充足的服务吞吐及带宽。

  3. 增量事件,避免消息体二次放大:vintage 内部将全部的数据变更,统一抽象为独立事件并在集群数据同步,一致性修复和服务发现 & 订阅功能上均采用增量通知方式,避免了消息体放大问题。将整体消息量有效压缩 2-3 个数量级,降低带宽的使用。而发生事件溢出时,系统也会先对全量数据压缩后再进行传递。

“保”是通过系统多重防护策略,保障通知风暴下 vintage 及微服务的状态稳定。其主要采用的三种策略:

  1. 首先使用 partion handler 实现对 vintage 集群内分区和微服务与集群节点分区网络问题的综合监控。结合 freeze 机制有效避免了微服务节点状态的频繁变更从而同样避免了在网络 QoS 引发的通知风暴。

  2. 其次对于业务系统来说,系统整体服务状态相对稳定。基于这点 vintage 为服务增加了保护阈值的设计(默认 60%)。通过设置保护阈值,可有效控制通知风暴发生时 vintage 内部异常服务数据变更的规模。更保障业务系统的整体稳定,避免发生服务雪崩。

  3. 最后对于带宽枯竭问题,vintage 增加了对带宽使用率的检测和限制。当超过机器带宽消耗>70%,会触发系统 304 降级,暂停全部通知推送,避免 vintage 节点带宽枯竭保障集群角色心跳维护,节点发现,数据同步等内部功能对带宽的基本要求,维护整个集群的稳定运行。

数据一致性

突发热点事件下微博高可用注册中心vintage的设计&实践

不难看出 vintage 是基于 AP 模型设计的注册中心。该模型下注册中心内节点间数据的一致性问题,成为了 vintage 必须要处理的核心问题。vintage 选择了最终一致性作为集群的数据一致性模型

接下来介绍 vintage 的最终一致性实现机制。

首先来回顾下 vintage 集群的部署拓扑。vintage 采用多 IDC 部署,各 IDC 间分治独立,IDC 间通过主主节点完成数据同步,IDC 内主从角色读写分离。在该拓扑下,vintage 通过集群内主从,集群间主主的数据同步 + 差异对比修复相结合的方式,实现节点间数据的最终一致性。(1) 数据同步:各 IDC 内均由主节点负责上行请求处理,从节点通过拉取主节点新增的变更事件完成数据同步(2) 同时 vintage 内部对全部存储的数据构建了对应的 merkle Tree,并随数据变化进行实时更新

突发热点事件下微博高可用注册中心vintage的设计&实践

vintage 会定期触发集群一致性检测逻辑。以主节点中数据为标准,IDC 内采用主从,IDC 间则是主主方式使用 merkle tree 特点由根节点逐层对比,精确定位数据差异节点,并完成一致性修复。

:通过线上运行统计,通常仅有<0.5% 的检测结果出现数据不一致现象。利用 merkle Tree 的特点有效优化了数据对比及修复过程中对网络带宽不必要的消耗:

  1. 大于 99.5% 一致性检测仅需对根节点数据对比即可确认数据一致性。

  2. 支持逐层节点对比,可准确定位不一致节点,并针对该节点进行数据修复。

高可用部署

服务的高可用通常与部署方式必密不可分,vintage 作为公司级别的注册中心本身必须具备极高的可用性和故障恢复能力。

vintage 在部署上主要考虑以下 4 点:

  1. 适度冗余度部署:冗余度方面,vintage 服务采用 2 倍冗余部署,冗余的节点在提升注册中心自身可用性的同时,也为业务系统在突发峰值流量时的快速扩容反应提供了良好的弹性空间。

  2. 与网络消耗型业务隔离:作为带宽敏感型服务,vintage 在机器部署时会与网络消耗类业务尽量隔离

  3. 多机架部署,同机架内多服务部署:考虑到网络分区和设备故障的因素,vintage 采用多机架部署方式,同机架内通常部署至少两个服务节点。

  4. 多 IDC 间数据互备:vintage 天然实现了 IDC 间的数据互备,同时通过冗余一套 IDC 子集群用于全集群的数据灾备。极端情况时可在分钟级实现 IDC 维度的任意子集群搭建,并通过系统内部接口优先从数据灾备的子集群中完成数据恢复。

2.5 高性能

在介绍 vintage 高可用后,再来看看 vintage 系统在高性能方面如何实现 10w+ 节点的支撑及平均百毫秒级别的通知延迟。

  1. List+Map 数据结构实现了支持 10w 节点高性能定时器。

  2. watch 变更实时推送机制。

    突发热点事件下微博高可用注册中心vintage的设计&实践

了解下微服务在 vintage 系统中的生命周期,这是一张 vintage 内部维护微服务生命周期的状态机。其中 initial,working,unreachable 为 vintage 内部对管理微服务的三个状态,分别用于说明节点处于初始注册状态,可用状态,以及不可用状态。

微服务节点通过调用 register 接口完成注册,vintage 会将节点状态设置为 initial。注册成功后微服务节点会周期性(例:5s)向 vintage 发送心跳,汇报自身健康状态,并不断更新自身心跳过期时间。vintage 在收到第一个心跳请求后,会将节点状态变为 working。vintage 会定期对全部 working 状态微服务节点进行健康检测,当发现节点心跳超时,该节点状态将被变更至 unreachable,并通过 watch 接口将变更时间推送给订阅方。而 unreachable 状态可通过再次发送心跳,转变为 working 状态。在 working To unreachable 的变更过程中如果触发了 vintage 节点状态保护机制,或出现网络分区,状态变更会被冻结。微服务节点下线时可通过调用 unregister 接口实现注销,完成整个生命周期。

vintage 每个 IDC 中仅由主节点负责全部的上行请求并通过心跳方式维护该 IDC 内全部微服务的健康检测及状态变更。而心跳作为周期性的高频请求,当单 IDC 内节点数达到一定量级时,对主节点的处理能力带来极大的挑战。并直接影响到单 IDC 集群的整体吞吐和服务节点承载能力。这就需要一个高效定时器来完成上述的挑战。

突发热点事件下微博高可用注册中心vintage的设计&实践

从图中可以看出,在设计上要求单 IDC 集群同样可承担 10w 级的服务节点,支持频繁的 expire 更新,同时要求对节点状态变更精度达到 10ms 级别。

首先对现有的 heartbeat 汇报及超时检测模型分析后,得出以下几个特点:

heartbeat 汇报请求时间,为节点 expire 的续租起始时间。

由于 heartbeat 续租时间固定,节点过期时间可根据续租时间固定排序。

健康检测与心跳独立处理。采用周期性触发机制实现对已排序数据 expire 的超时判断。

通过对特点的分析,并参考了目前比较流行的一些定时器处理算法。不难看出链表,最小堆以及时间轮这些主流定时器的时间复杂度上均会受到节点数的直接影响。那是否有一种数据结构可以做到 update 和 trigger 都是 o(1) 的时间复杂度呢?

突发热点事件下微博高可用注册中心vintage的设计&实践

定时器(timer)由 List+Map 共同组成用于维护 vintage 内部所有 working 状态的微服务节点的健康监测和心跳续租。

List 是一个根据 expire 升序排列的有序双向链表,链表中的元素为微服务节点的状态对象,包括节点 ID 和超时时间。Map 保存了微服务的 ID 及状态对象的引用。当 vintage 收到某个节点的心跳请求,会根据节点 ID 从 Map 中获取该微服务节点状态对象,由于心跳续租时间固定,完成 expire 字段更新后无需排序,可直接将节点对象插入在链表的尾部。

timer 会定期触发微服务的超时检测,根据链表 expire 升序的特点,每次检测的顺序都是由首都到尾部,发现首节点 expire 小于当前时间,触发过期操作,将节点从列表中删除,并调用 callback 函数,通知存储模块将节点状态更新为 unreachabl。依次向后检查链表中的各节点,直到出现第一个未过期的节点为止。

突发热点事件下微博高可用注册中心vintage的设计&实践

当服务状态变更,节点的注册与注销时都会将数据记录到存储中。vintage 实现了一套基于树形结构的多版本数据存储。依赖于树形存储结构,vintage 在实现对数据存储能力外,还可将微服务依照 /IDC/ 部门 / 业务线 / 服务 / 集群 / 服务节点 的方式分层管理。同时借助于多版本实现了从数据到事件的转换,并通过 Event notify 模块将变更事件实时回调通知 watch hub,完成对新增事件的订阅推送。

watch 推送机制

突发热点事件下微博高可用注册中心vintage的设计&实践

client 注册 watchhub

client 向 vintage 订阅服务变化过程。会首先向 vintage 的 watch hub 进行注册,watch hub 会为每个订阅方生成一一对应的 watcher 对象,并根据订阅目标的 path(ID),将相同路径的 watcher 合并至一个 watchers 列表中,保存在 watch hub 的 map 索引结构。

突发热点事件下微博高可用注册中心vintage的设计&实践

存储层数据变更通知

当 storage 完成数据存储后,会将数据变更的信息转换为新增事件通过 Event notify 模块回调通知 watch hub。watch hub 通过该 event 中的 path 在 map 索引找到订阅的 watchers 对象,并将事件写入全部 watcher 的 event queue 中。由 watch 函数完成事实数据推送。

:若 path 是多层结构时,watch hub 通过逆向递归的方式,将该事件依次插入多个 event 队列,实现 watch hub 的 path 递归功能。

系统伸缩性

为保障微服务的快速扩容,vintage 服务自身必须具备快速的扩容能力 vintage 会通过 Docker 化部署及 node discovery 节点自动发现能力,实现秒级别的扩容。并通过整合 Jpool+Dcp 体系,目前可实现分钟级 IDC 注册中心搭建。

多语言支持

在多语言支持方面,vintage 系统通过 HTTP Restful API 满足了公司级跨语言注册中心的服务支持。同时也在不断扩展多语言官方 SDK,目前已实现了 Java,Go 的支持。

 三、总结 

新版 vintage 系统已在线上运行了一年多,经历了微博春晚保障,多个核心机房网络升级和数不胜数的突发热点事件应对。

vintage 系统使用多 IDC 部署方式支持公司混合云战略,承担十万级别微服务注册与服务发现。同时系统可用性达到 99.9999%,通知变更平均延迟< 200ms,p999 延迟低于 800ms。vintage 注册中心在可用性和性能上,也满足了业务在突发热点时的应对保障,将常备业务机器的 buffer 由 40% 降低至 25%。

嘉宾介绍
突发热点事件下微博高可用注册中心vintage的设计&实践
边剑:新浪微博技术专家,专注于高可用架构,有多年高并发系统架构设计和研发经验,现就职于微博研发中心 – 平台架构部,主要从事微博平台公共服务及中间件系统的设计及优化,作为核心技术成员参与微博服务化建设。

快手服务治理平台KESS的设计理念和实战

快手,记录世界,记录你。这是短视频平台快手的 slogan,它的背后有着复杂的技术挑战。

如今快手有 80 亿条海量短视频,1.5 亿的日活,每天仍然有超过 1500 万条以上的新增短视频。如何根据用户的个性化需要,把海量视频精准的分发给用户,让有户得到独特的体验,感受科技带来的幸福感,这是一个复杂的技术问题。快手的用户界面非常简洁,在简洁接口背后是非常复杂的一套后端系统。

以下内容是快手科技基础平台架构师曹福祥在 2018 北京 ArchSummit 全球架构师峰会快手科技技术专题中分享,快手服务治理平台 KESS 的设计理念和实战。

快手服务架构和服务化背景
快手的公司使命是用科技提升每一个人独特的幸福感,这其中就包括让每一个普通人都有机会被世界所看到,以及看到更广阔的世界。这里需要解决一个问题,如何让所有视频都有机会被看到,同时也不能让低质量或者用户不感兴趣的视频影响用户的体验,换句话说,如何解决十亿级长尾视频的高效分发。

快手服务治理平台KESS的设计理念和实战

为了解决这个问题,快手采用了独特的技术架构,一是前端入口非常简洁,关注、发现、同城三个 Tab,二是极其复杂的后端架构,通过 AI 和大数据技术实现视频内容理解和个性化推荐,从而解决内容分发的问题。

快手的后端服务架构包含多个业务模块,比如视频处理、推荐、广告、消息系统等,每一个模块都可能由成百上千的微服务组成。这是一个非常复杂的微服务网络,并且其规模也随着用户规模和开发团队扩张而增长。如何在保障整体服务质量的同时,还能保证新业务的开发效率和质量,需要一个统一的服务治理方案,来解决微服务开发运维过程中的各类问题。

服务治理平台方案选型

快手服务治理平台KESS的设计理念和实战

这里列出了服务治理的几个基本要求。首先要有完善的基础平台和组件,包括配置中心,服务发现、监控平台,以及服务开发框架。第二,支持多语言,因为快手的业务特点,主要使用的语言有 Java 和 C++,还有少量业务会使用 Node.js、Python。第三,高可用、高可伸缩性。第四,快手的服务原来主要部署在物理机和虚拟机上,现在又增加了容器化部署平台,需要兼容所有的部署平台。

除此之外,还有几个核心痛点,第一,服务治理平台自身的可用性要达到足够高。第二,要支持跨数据中心的路由管理。第三,有状态服务管理,这部分在大部分常见方案中支持不多,后面详述。第四,复杂服务调用网络上的质量监控。

这是四个核心痛点,加上前面的四个基本要求,根据这些依据来做服务选型。

首先快手调研了一些开源的方案,看看是否可以做一些定制化的开发,这样做的优点是有大量的业界经验可以借鉴,可以少踩坑,前提是改造成本不能太高。

常见的开源方案有很多,为了方便比较,快手把常见的服务治理方案从需求出发做了一个简单的分类。

快手服务治理平台KESS的设计理念和实战

  1. 第一类是简单的基于分布式协调系统的方案,比如基于 Zookeeper 的临时节点来做。这类方案缺点比较明显,之前 Netflix 和阿里都有专题文章分析了这类方案的弊端,从 CAP 理论的观点来讲,配置中心和服务发现需要尽量高的可用性和分区容忍度,一致性要求相对较低,一般认为最终一致即可。这与 Zookeeper 的设计模型有一定的偏差。

  2. 第二类是服务发现和配置管理中心方案。比如国内很多公司使用的 Consul,以及今年阿里刚刚开源的 Nacos。

  3. 第三类是集成服务治理的单语言 RPC 框架,比如说 spring cloud,还有 Dubbo。

  4. 第四类是集成服务治理的多语言 RPC 框架,比如腾讯 2017 年开源的 Tars。

  5. 第五类是容器化的平台。Kubernetes 和 Istio。前者是大家比较熟悉的一个容器化管理平台。Istio 是基于 Kubernetes 的一个 service match 实现,今年 7 月刚刚发布了第一个正式版本。

这些方案,有的在基础需求上有所欠缺,比如说不支持多语言或者缺少一些组件;还有一些在核心痛点上没有足够的支持。比如,服务治理平台本身的可用性,需要从底层做大量的改造,开发成本非常高。

经过调研,快手决定自研,自研除去解决基本需求和核心痛点之外,还有一个附带的好处,能够快速迭代,能够在业务发展非常快的情况下,快速跟进业务提出的各种需求。

痛点分析和设计理念

下面分析一下四个核心痛点,它们从何而来的,自研的 KESS 平台又是如何解决这些问题的?

痛点一:服务治理平台自身的可用性。服务治理平台非常基础,一些公司把它称为元服务,即服务之服务,它的可用性决定了业务服务可用性的天花板。首先看一下 KESS 的多数据中心的架构。

快手服务治理平台KESS的设计理念和实战

快手有国际化业务,把相对独立的国家和地区称作 division。比如图中 central division 是指国内的,另外还有韩国、印度等。在每一个 division 内部会有多个数据中心。其中有一个是主数据中心,其他的是从数据中心。KESS 负责从主数据中心到从数据中心的数据同步。主数据中心是全局配置唯一的可写入端。一旦主数据中心发生了灾难,可以将另一个可用的数据中心切换为主,在较短时间内恢复功能。这里比较核心的一个高可用设计,在于它的配置分发部分。

快手服务治理平台KESS的设计理念和实战

上图展示了主从两个数据中心的情况。底层的存储虽然使用了 ZK,但使用方式非常节制,仅仅把 ZK 当做一个小数据量的 KV 存储,由上层来处理多个机房之间的数据同步。并且不依赖于 ZK 的通知机制,采用既推又拉的方式去同步数据。事实上,可以把 ZK 换成任何能够满足要求的存储系统,包括 MySQL。

在底层的存储之上,设计了一个配置同步协议,这个协议里定义了一个最小同步单元,其中所有配置项的修改是原子可见的,整体上满足 BASE 原则,即基本可用,软状态,最终一致性。

Mandator 是每个数据中心的协调模块,是主从热备的,负责数据同步。每一个机器上还会运行一个 Agent,Agent 负责同步数据到本地的文件系统和共享内存中。SDK 定期扫描本地缓存,获取数据更新。

这里,第一解决了多数据中心数据同步的问题,第二实现多层的缓存以及持久化,第三,任何一个模块如果出现了故障,有备用的模块,如果没有备用模块,也有一个缓存,保证它的读可用性。

在配置分发之上建设了服务发现机制,在设计中也包含了一些中间缓存来确保某些模块暂时失效时,业务的无感知。

痛点二:跨数据中心的路由管理

快手服务治理平台KESS的设计理念和实战

对于多数据中心部署的服务,通常是需要就近访问的,一方面能保证低延迟,另一方面也能减少专线故障对可用性的影响。但也有一些例外,比如说机器资源可能不均衡。在快手,因为业务规模扩张太快,经常出现某个数据中心机器资源紧张的情况。一些服务在特定数据中心没有机器可以部署,就近访问就会失败。另外,在节假日,用户访问量通常会增长,需要提前估算流量安排机器扩容,但估算可能会不足。

再一个,下游业务可能会发生故障,这个时候需要把原来就近访问的请求分发到其他可用的数据中心去。所以我们提供了这样的功能,在正常的情况下都是就近访问的,但是遇到突发情况,能够快速把某个服务的流量按比例调度到其他数据中心去。

痛点三:有状态服务管理

有状态服务管理是较少提到的话题,因为它比较复杂,一般在业务架构设计中是要尽量避开的。为什么快手需要有状态服务,主要分为两种情况,一是在特定的领域存在有状态服务的开发需求,比如高并发的消息服务、推荐系统、多媒体数据理解等。二是业务服务无状态,实际上是把状态交给了分布式存储。当业务规模不断扩大的情况下,已有的分布式存储方案可能不是特别适用,需要做定制化开发。

快手服务治理平台KESS的设计理念和实战

一个典型案例,快手的私信服务,可以理解为是一个 IM 服务。这个服务管理了用户手机和快手服务之间的长连接。长连接就是一个状态,长连接建立好之后还会有一个用户会话数据,这也是状态。一旦服务重启,这些状态就会丢失,用户就会掉线,虽然可以在客户端做一些 cover,但不是特别理想。

所以把这个状态和业务逻辑做了解耦。一是把长连接的管理拆分为一个服务,它逻辑非常简单,非常稳定,很长时间都不需要去更新它。二是把业务逻辑做成一个无状态的模块,它更新比较快,但是重启也不会导致用户掉线。三是开发了一个 Crux 服务存储会话数据。它是持久化的,支持多地多活,能支撑千万级的读写 QPS。为此我们牺牲了写读一致性,写入之后可以容忍在十毫秒之内读到即可。它是一个非常定制化的分布式存储服务。Crux 服务目前支撑了主 APP 的私信服务,同时有千万级用户在线,1.5 亿的日活。

对于 Crux 这样的有状态服务的开发,有一些共通的做法。首先要对这个数据做分片,让它可以伸缩;然后需要主从多副本,能够支持自动 Failover。还要支持平滑扩缩容。以及对于快手来讲,一定要支持多数据中心。KESS 将这些业务无关的逻辑抽象为一个统一的有状态服务管理模型:预分片、自动平衡以及状态迁移,来帮助用户简化有状态服务的开发。

痛点四:复杂服务网络的监控

快手服务治理平台KESS的设计理念和实战

在复杂服务调用网络上的质量监控,主要有如下一些需求:

  1. 服务依赖梳理,在做服务改造或者容量预估时,需要了解上下游的情况。

  2. 如何监控服务整体质量指标,而不是单机指标?

  3. 出了故障,怎么定位?

为了解决这些问题,我们开发了 RPC Monitor 监控系统,它主要有以下几个核心功能。

  1. 能够实时的查看整体服务的可用性
  2. 对可用性做多维的分析。比如除了实例维度之外,还要能够对机房维度、错误类型维度、主被调的不同维度来做分析,帮助快速定位问题。

    快手服务治理平台KESS的设计理念和实战

  3. 调用链分析。比如上图是现实当中某一个服务上下游的情况,其中红色的小点代表微服务,背后可能有成百上千的实例,线条代表调用关系,线条的颜色代表调用的成功率。

  4. 报警功能。上图右侧是一个手机报警页面。从图中我们可以看到成功率曲线的陡降很可能是跟 QPS 上涨有关。工程师也可以通过下面的图表和链接去查看更多维度的信息来判定问题根源。

KESS 平台应用现状和未来的计划
快手服务治理平台KESS的设计理念和实战
KESS 的整体架构

上图是 KESS 平台整体的架构。KESS 目前在快手得到了广泛的应用。目前 KESS 上管理了几千个微服务,几万台服务器,在四个国家和地区都有部署,分布在十多个数据中心。

平台的整体可用性是 99.997%,之前发生过一次 Zookeeper BUG 导致的故障,写可用性受到了影响。

跨数据中心的路由管理功能在快手内部每个月大概有一百多次的使用。RPC Monitor 每天会发出几百个报警信息,发现并定位了后端工程方面几乎 100% 的故障。

关于未来计划

目前快手有更多的新语言开发者加入,需要服务平台继续支持更多的编程语言。服务网格是业内比较关注的领域,它能有效地提高工程师的开发效率,目前我们也在探索其结合方式。再有,多数据中心给业务架构设计增加了很多复杂性,我们希望能在基础平台的层面做更多的适配,简化业务设计开发的负担。

嘉宾介绍
快手服务治理平台KESS的设计理念和实战

曹福祥,快手基础平台架构师,快手服务治理平台技术负责人,快手技术培训资深讲师,擅长分布式系统、大数据、微服务框架等技术领域。曾经在网易、微软、小米负责基础架构相关工作。