Lion 设计与实现
本文是在美团内部面向其他团队的分享
1-介绍
1-1 功能介绍
Lion 是一个面向分布式系统的配置中心,采用中心化数据管理,通过 C/S 架构实现配置的统一管理和实时推送。
应用程序使用 Lion 可实现配置与代码分离、配置变更推送、历史版本管理、灰度发布等功能。通过集中化的在线配置管理,应用可以实现秒级配置变更,并且保证所有节点配置的最终一致性。除此之外,Lion 还提供操作日志查看、灰度发布、配置审核等高级功能。
1-2 SLA 指标
指标 | 目标 | 说明 |
---|---|---|
推送成功率 | 99.9% | 配置修改成功后(5s内)所有客户端成功获取到最新配置的比例 |
推送时效性 | <5s | 配置从修改成功到客户端获取到新配置的时间 |
当配置进行一次成功变更之后,Lion 只能保证最终一致性,即所有 Client 最终都能获取到最新配置,不能保证强一致性。
1-2 服务端性能指标
指标 | 数据 | 说明 |
---|---|---|
单机/集群写性能(8C8G) | 700QPS/2500QPS | HTTP Open API |
单机读性能(8C8G, RT < 100ms) | 7000QPS | HTTP Open API |
单机最优连接数(8C/16G) | 8000 | 服务端长轮询连接 |
北上同步延迟(1700QPS) | TP999<500ms | 数据在一侧数据中心发生变更后,同步到另一侧的延迟 |
2-系统设计
2-1 整体架构
Lion 系统采用异地多活架构:北京 & 上海各部署一套数据中心,支持双写,通过自研同步组件(Consistency)保证两个数据中心间数据一致性。业务配置存储主要使用 MySQL, Redis 作为存储介质(ZK 即将下线)。
用户可以通过管理端(Console)与 HTTP 接口(API)进行配置变更与查询操作,同时 Lion 也支持多语言的 SDK(Java,Node,C++,Golang),便于业务服务进行配置获取与变更监听操作。
模块名称 | 主要功能 | 备注 |
---|---|---|
lion-config | 用于 client 获取配置与监听配置变更 | client 与 config 使用长轮询通信; config 集群北上多机房部署,支持多集群 |
lion-consistency | 不同数据中心间数据同步; 同一数据中心,不同存储介质数据同步 | 北上双集群部署 |
lion-meta | 实现 config, consistency 服务发现功能 consistency 同步数据分区策略控制; 用于 client 获取相关元数据:config 节点列表,黑白名单项目等 | 弱依赖 mns 实现节点注册发现功能,在 mns 不可用时降级到自研健康检测策略 |
lion-client | 与 config 服务交互实现配置获取/监听功能 | 定时从 meta 获取可用 config 节点列表,并通过长轮询访问 config 服务端; config 服务不可用时,从本地文件获取配置(可能是过期数据); 支持多语言 sdk: java, node, go, c++ |
lion-api | 提供 http open api:用于业务方通过 http 实现配置的变更与查询 | |
lion-console | 管理端交互,提供配置增删改查功能 |
2-1 配置获取与推送
Client 在获取配置时,加载策略依次为:1. 内存缓存,2. Lion 远程服务端,3. 本地持久化文件。在从服务端某个节点加载失败时,会及时切换到其他可用节点,直到超时;之后会降级到本地文件(可能仍是上次获取到的旧值)。
Config 服务端在收到 Client 获取配置的请求后,依次从:1. 内存缓存,2. Redis,3. DB 中加载配置。配置加载成功之后,需要比较 Client 携带的配置版本号(c_version)与服务端加载到的配置版本号(s_version):
- 如果 c_verison == 0,则说明 Client 首次获取该配置,此时直接返回服务端配置
- 如果 c_version != 0 且 c_version < s_version,则说明该配置在 Client 上次加载之后又更新过,此时需要将最新配置返回
- 如果 c_version != 0 且 c_version >= s_version,则说明 Client 本地配置已经是最新值,此时请求被 hang 住,直到该配置被更新或者请求超时(100s)
2-2 一致性保障
Lion 配置存储分为北京/上海两个数据中心,每个数据中心分别有 DB, Redis, ZK(即将下线) 。为了实现异地多活,需要保证北上数据的一致性。
Lion 采取的是最终一致性,一致性保障由 Consistency 模块维护,整体可以分为两个方面:
- 不同数据中心间的一致性
- 同一数据中心内不同存储介质的一致性
2-2-1 同步模型
为了实现数据最终一致性,lion 对每次配置变更都会记录一条 log,被称为同步日志(sync log)。该 log 模型基本如下:
key | release_key | local_db_status | remote_db_status | zk_status | redis_status | status |
---|---|---|---|---|---|---|
配置唯一标识 | 配置变更唯一标识 | 同侧 db 状态:0 未同步,1 同步成功 | 异地 db 状态:0 未同步,1 同步成功 | 同侧 zk 状态:0 未同步,1 同步成功 | 同侧 redis 状态:0 未同步,1 同步成功 | 本次变更同步状态: 0 未加载,1 已加载未同步,2 同步成功 |
status 字段用于标识同步流程是否全部成功,如果同步失败,则会重试直到同步成功。
不管是通过 http api 接口还是管理端变更配置,只会更新同侧的 db,并不会同步更新同侧数据中心内的 zk, redis(提高接口性能)。每次配置变更产生的 log 为:
local_db | remote_db | zk | redis | status |
---|---|---|---|---|
1 | 0 | 0 | 0 | 0 |
2-2-2 同步流程
同步流程步骤分为:
- log 加载:将未同步完成的日志从 db 中加载到内存队列中
- 异地同步:优先将 log 批量同步到异地;异地同步成功后将 log hash 到内存队列中
- 本地同步:将 log 顺序同步到本地 db, redis, zk 中
结合 log 中各个同步状态进一步分析:
- 本地 db 同步:如果本地 db 未同步,则首先同步 db。如果同步成功,则 log.local_db = 1。(对 log 产生侧来说,local_db 默认 1,即默认不同步)
- 异地同步:该流程为异步操作,仅仅是将 log 发送到异地并被接收,并在稍后将配置回写到 db, redis, zk。如果同步成功,则 log.remote_db = 1.
- 本地 redis 同步:如果同步成功,则 log.local_redis = 1.
- 本地 zk 同步:如果同步成功,则 log.local_zk = 1.
3-可用性
承诺 Lion 服务的可用性不低于 99.99%。
模块名称 | 集群分布 | 外部依赖 | 内部依赖 | RTO | 故障时主动切换 SLA |
---|---|---|---|---|---|
lion-consistency | 两地多机房 | MySQL、S3、专线(弱)、DNS(弱) | lion-meta | 120s | 0s |
lion-api | 两地多机房 | MySQL、S3、Redis(弱) | 120s | 0s | |
lion-config | 两地多机房 | MySQL、S3、Redis(弱) | lion-meta | 120s | 0s |
lion-console | 两地多机房 | MySQL | lion-api | 60s | 0s |
lion-meta | 两地多机房 | MySQL、MNS(弱) | 60s | 0s | |
lion-client | - | DNS | lion-config、lion-meta(弱) | T_{断连感知}=120s | 3s (timeout)+3s * (0~N) (N:异常 Config 数目) |
config 服务北上两地多机房部署,支持弹性伸缩:
- 对于异常 config 节点,meta 会及时剔除,保证下发给 client 节点列表的可用性
- client 在请求 config 服务端获取配置时,优先访问同地域节点;同地域无可用节点时,访问异地节点
如果所有 config 节点均不可用,则降级到本地持久化文件
config 服务加载配置时,强依赖 db,s3(文件配置),弱依赖 redis:
- redis 不可用时,对配置获取 & 推送没有影响
- db,s3 不可用时,client 无法获取到最新配置(通过 config 内存缓存可以获取到旧配置)
consistency 服务北上两地多机房部署,支持弹性伸缩:
- 同步 log 加载强依赖 db,db 不可用时,同步任务无法执行
- consistency 节点异常时,meta 会及时剔除,并更新同步策略(尽可能降低节点变更对同步延迟的影响)
4-隔离性
配置隔离可以从两个方面来看:
- 配置获取:客户端通过长轮询访问 lion-config 获取目标配置
- 配置同步:配置变更在北上两地/同一数据中心内保持一致
4-1 配置获取
config 服务支持多集群,不同集群管理特定业务配置。对于一些配置容量大/稳定性要求高的业务,会拆分出独立集群,其他业务共享默认集群。
config 集群 | 集群所属项目 |
---|---|
zebra | {ds, group-ds} |
buffalo | {buffalo-reader-task,buffalo-client,buffalo-writer-task} |
waf | {com.sankuai.sec.waf.manage} |
default | {others} |
各个集群配置管理相互独立,业务流量隔离:
- buffalo 配置获取只会访问 buffalo 集群,同时 buffalo 集群只管理 buffalo 相关的配置
不过,各个 config 集群依赖的底层存储(redis, db)仍共用一个集群
4-2 配置同步
由于不同业务对推送延迟的敏感程度不同,因此我们将配置同步分为两种:1. 普通配置,2. 高优先级配置;这两种配置同步流程相互隔离。
通过将不同配置的同步流程隔离,使得普通配置的堆积并不会对高优先级配置的同步流程产生影响。
4-2-1 局限
不管是配置同步还是配置推送,目前都是在业务逻辑层进行隔离,在底层存储方面,所有业务共享一套存储。因此,目前针对业务隔离仍存在一定的局限性,如 db 集群异常时,所有业务方都会受到影响。
不过,鉴于当前单集群存储容量在中短期内仍能支撑业务增长,并且存储层的可用性也能满足 Lion 的需求,因此短期内仍会保持现有隔离方案。
5-可观测性
5-1 推送链路
对于每次配置变更的推送情况,Lion 提供了推送链路的功能帮助用户排查配置的推送情况,包括:
- 推动成功率,推送延迟,每台机器的推送详情等
不过,该功能对 lion-client 版本有所要求(>=0.8.15.4),目前覆盖率 >80%(各版本分布情况).
5-2 数据同步
对于配置同步,有两个关键指标:1. 同步延迟,2. 同步成功率。
- 同步延迟的大小与配置大小,配置的变更频率,同步批次大小,同步任务间隔等有关。经过测试,对于小配置(<1KB),在 1700 QPS 下延迟 TP999<500ms,最大延迟 1.8s。
- 通过一致性大盘可以看到,数据一致性 >99.99%
6-FAQ
- 业务在接入 lion 时,会按照 key 的粒度编码获取配置,如果业务方代码中获取引用了大量 key,是否会导致不同 key 每次都通过 http 访问 config 服务端是否导致耗时增大?
- 如果同一个项目调用了多个 key(>5),那么配置获取粒度会由 key 升级为项目/分组,也就是说会从 config 服务端一次获取整个项目/分组的配置;这样当访问其他 key 时,只需要从本地内存缓存即可获取,减小了配置获取耗时。
- 如果 lion 服务端均不可用,对业务方有何影响?
- 从配置获取来说,在服务端完全不可用时会降级到本地持久化文件,可能会获取到已经失效的配置,并且不会感知到最新的配置变更
- 除了服务端内部一致性,如何保证客户端与服务端间的数据一致性?
- 增量同步:客户端通过长轮询周期性地与服务端通信,请求信息携带了当前客户端配置的最新版本号,服务端通过比较版本号来判断客户端的配置是否过期,如果过期则更新客户端
- 全量同步:客户端定时全量同步本地配置,此时直接从服务端获取全量配置并更新本地过期配置
- 北上数据同步时,如何保证同一条 log 不会被重复同步?
- log 通过 status 区分同步状态;同步任务只会加载 status = 0 的记录,确保同一条 log 不会被重复加载
- log 被加载后会进行分区,保证不同 consistency 节点不会同步相同的 log
- 如何保证配置变更一定会被同步成功?
- 有三种同步机制:1. log 创建后被立即同步;2. 同步失败后会进行有补扫任务进行再次同步(50次);3. 北上定时检测机制,对于不一致的数据会重新触发同步流程。
- 北上同步时,如何解决数据冲突?
- 冲突解决方式为:最后写胜出(lrw),以最新版本为主。不过,两地同时写入的概率较低。
- 在上海侧的一次变更,需要多长时间才能推送到北京侧业务机器?
- 北上同步延迟 TP999 < 500ms,我们承诺 99.9% 的配置会在 5s 内会全量推送到所有机器(具体每次配置变更推送延迟可以查看:Lion 推送链路)
7-未来规划
中短期来看,Lion 规划主要有:
- 提升用户体验:丰富多语言客户端功能,提升MWS易用性
- 提升系统可观测性:增强配置链路跟踪能力,提升监控能力,
- 提升容灾能力
- 提升系统效率:对齐业界同类系统,提高系统支撑能力、提升系统效率
- 提升平台化能力:增强存储及流量隔离,增强集群监控、运营统计,开放集群拆分