卫旋,B端技术中心资深开发工程师,S12技术保障项目总负责人。
一、前言
英雄联盟全球总决赛是一年一度最为盛大的电子竞技比赛,在国内关注度极高。11月6日,DRX战队以近乎奇迹的方式,一路从入围赛披荆斩棘拿下了S12全球总决赛的冠军。相当励志,恭喜DRX!
B站作为今年S12的官方直播渠道,哔哩哔哩赛事直播间实时人气一度超过3.1亿。如何保障整个S赛洪峰流量下系统的稳定性和流畅性,给我们带来了巨大挑战。
为此,我们在7月底成立了S12技术保障专项,展开了为期3个多月紧锣密鼓的技术筹备,我们的目标是可以实现“喝茶保障”。
二、如何实现
经过盘点,我们把S12技术保障依赖的各项工作拆分为赛前、赛中、赛后三个阶段,如下图:
1、项目启动
S12技术保障需要公司多个技术团队之间的紧密配合,涉及业务研发、SRE、基础架构、DBA、大数据等近300人。从上游业务充分搜集S12相关资讯后,我们召集所有相关团队负责人召开了项目启动会,最终在S12技术保障目标上达成一致,争取做到全局步调一致。
2、资源预估
1)贴合业务
为了发挥资源的最大使用价值、提高资源利用率,我们与上游业务对齐了业务目标PCU(最高在线用户数)、后续宣发策略和规划,方便技术侧及时介入准备、做出合理的资源估算。
2)合理估算
从B站SRE容量平台查询到去年S11服务器峰值用量a、去年8月日常峰值用量b,粗略算出同期增长系数delta=(a-b)/b+1。
同时查询到今年8月份日常峰值用量c,服务器当前可用量d。因为我们要求服务器容量安全水位在40%以下,所以缺口资源量=c*delta/0.4-d。
产品功能在保障过程中也会有增加,因此这部分增加的功能所需的资源支持会在单独评估资源缺口后追加申请。
3、资源池治理
去年以前,直播资源池都是直播专属、与其他部门独立的,当直播资源池资源用尽后,需要从其他资源池协调机器->网络测试->机器初始化->重新打label入直播池,全程人工、非常低效,经常导致线上业务发布或扩容阻塞、影响应急响应效率。
从全站视角看,大型赛事直播结束后大量用户会从直播间转向社区其他服务(稿件、评论、专栏、动态等),类似“潮汐流量”,这部分服务器冗余可以通过合池来“降本”。合池的前提是机器运行时的标准化:
1)服务去CPUSET
由于直播的实时性和对延迟的敏感性特征,我们的业务场景无法接受高概率的CPU Throttle带来的超时。大量服务使用了CPUSET的绑核策略,CPUSET无法资源共享、无法合池、造成一定的资源浪费。
内核同事协助排查后发现,PHP服务的高概率CPU Throttle是因为同时并发执行的R进程过多,导致CPU Quota被击穿。排查PHP基础库发现,直播PHP服务使用了Swoole多进程模型,基础库会在每个Worker进程内设置一个定时器用来更新配置,1s检查一次,多个进程大概率会在同一时刻被唤醒。修复方案有两个:1)打乱进程定时器执行的随机性,减少“碰撞”;2)基于inotify进行配置更新,废弃基础库定时器。因为业务代码里也大量使用了定时器用来对直播热点数据做内存预加载,所以最终是采用了方案1,更新基础库后效果非常明显。
protected function funcWorkerStart()
{
return function (\swoole_server $Server, $iWorkerID) {
// 部分服务会在init_callback中注册worker timer做配置加载
// 但是会带来并发R过多导致cpu throttle,此处做下随机sleep 10-500ms
mt_srand((microtime(true) + posix_getpid()) * 10000);
usleep(mt_rand(10000, 500000));
Main::init();
//配置文件定期加载
$config_reload_cb = function () use ($C, $Server, $iWorkerID) {
app(Metrics::class)->flush();
// load config...
// config check...
// reload worker...
};
//注册定时器
swoole_timer_tick(1000, $config_reload_cb);
};
}
对于Golang服务,我们引入了automaxprocs来自适应调整在Linux CFS调度模式下的GOMAXPROCS,并对所有Go服务做了代码lint检测。
2)宿主机内核升级
直播部分老机器内核版本过低无法参与合池,并且存在cgroup泄漏问题[1],造成业务超时抖动。为了合池后不影响其他部门在线业务应用,需要对老内核机器进行内核升级。
B站操作系统团队统一了内核版本,根治了困扰业务已久的cgroup泄漏问题,同时基于Group Identity对在离线业务混部做了CPU隔离优化。
完成直播PAAS合池后,缓解了部门之间机器多份冗带来的资源浪费。在线业务使用B站SRE容量管理平台按需分配,大大提高了资源利用率。
4、业务场景拆解
1)确定保障范围
B站直播经过8年的发展,已经具备了多样化的直播间玩法、能力,可以支持不同类型直播场景。今年的S12我们也推出了很多新玩法,涉及的各项能力要在对应的后台配置好后才会呈现给用户,不同的功能玩法由不同的技术团队支持,所以首先要确认S12用到的已有能力和新增玩法(共30+个),圈定保障范围。
2)确定场景分级
分级标准如下:
① P0
部门核心用户场景所对应业务:需满足月日均DAU >= xx W;如不满足条件,降级到L1
部门营收业务:需满足月日均营收 >= xx W;如不满足条件,降级到L1
虽未达到上述要求,但业务属于公司战略级方向
强依赖下游业务
② P1
部门L0业务主场景使用中依赖的主要业务
核心的二类业务
不满足L0的部门核心用户场景所对应业务
不满足L0的部门主要营收业务
强依赖下游业务
③ P2
部门给用户提供的其他业务
强依赖下游业务
如上所示,我们从DAU、营收数据、依赖等维度定义了服务的分级标准,按照场景拆分确定保障等级。最终根据S12涉及到的30+个功能拆解出10个P0场景、16个P1场景、9个P2场景。
对于P0和P1的场景要重点覆盖混沌工程测试、客户端性能测试、服务端性能压测、生产多活演练、可观测监控等,保证服务架构高可用。
3)梳理场景地图
① 定义
场景拆解后,针对单一场景将关联的业务逻辑(微服务调用关系、接口依赖等)进行全局梳理形成场景地图,帮助场景负责人和决策者快速了解服务本身的依赖情况。
② 梳理规范
场景名称:xxx
场景等级:P0/P1/P2
场景介绍
方式:通过抓包、代码翻阅、负责人确认的方式明确下述内容
方法:5w2h法(Who/When/Where/What/Why/How/How much)
场景依赖:接口、服务、缓存、数据库、消息队列
有了场景地图,对S12的技术治理和优化会更有针对性。整理场景地图的过程,一方面加深了研发、测试对于场景逻辑的认识,一些历史问题也会变得清晰;另一方面这些元数据有助于后面各项技术架构优化、监控元数据的校准。
5、高可用架构
分布式架构下微服务之间相互调用十分复杂,一个点位故障极有可能影响整条链路的稳定性,为此我们做了长足的架构演进准备。
1)单点治理
分布式架构下,单点本身不具备容灾能力,最容易出现问题。应该优先处理掉,主要有:
应用单点:要求所有应用都应该多实例部署(>2)。
JOB单点:基于XXL-JOB二次开发直播分布式任务调度系统,可以有效解决JOB无法多实例分片执行的问题。
资源池单点:直播PAAS资源池单宿主机巡检,避免机器单点故障后导致应用无法重新调度成功。
2)高在线自适应保护
千万直播场景下,一场直播结束会有大量用户退出直播间回到流量入口页,对其他网关及下游带来数倍的压力放大。因为Prometheus监控有采集窗口,实际的请求“毛刺”比下图所示还高。从流量转化数据上看,其实80%的请求是不必要的,用户可能已经关闭APP了。
针对这类Case设计了高在线自适应降级保护方案。通过服务端与客户端协议打通,直接从源头上避免不必要的用户热点行为、对后端服务器的流量冲击。
目前已经上线的保护策略如下,识别到当前直播间热点后:
退出直播间不自动刷新流量页
针对不重要的KV配置等请求,做随机延迟打散
针对广播集中触发接口热点请求,做随机延迟打散
动态调大离线数据上报间隔,降低服务器压力
API Gateway限流后自动触发客户端流控
3)混沌工程
在生产环境中运行分布式系统,难免会有各种不可预料的突发事件、故障发生,而微服务之间相互依赖,可能会产生异常连锁反应。我们应该致力于在这些异常行为被触发前,尽可能多地识别风险,然后针对性地加固防范,从而避免故障发生时所带来的严重后果。
混沌工程正是这样一套通过在分布式系统上进行实验,主动找出系统中脆弱环节的方法学。这种通过实证的验证方法可以为我们打造更具鲁棒性的系统,同时让我们更透彻的掌握系统运行时的各种行为规律,也能在这个过程中及时针对性的补齐系统预案。
B站从去年开始引入混沌工程,基于chaosblade二次开发,融合监控、问题管理形成初步满足业务微服务治理的故障注入平台。但渐渐发现chaosblade只能控制端口级别的故障,爆炸半径过大,影响较大无法在生产环境执行。今年自研了控制粒度更细、爆炸半径更小的混沌演练平台,可以控制到接口、用户粒度的故障。详见下图:
针对S12核心L0/L1场景,我们在进房、送礼、发弹幕、首页等场景进行了故障演练、红蓝对抗,主动发现并治理核心链路上的几类非预期问题:
代码问题,弱依赖被错误地实现为强依赖,导致核心链路不通;
弱依赖未考虑降级方案,用户体验不佳;
代码问题,对于弱依赖的降级方案不生效;
对于强依赖故障,客户端能否做到容错、友好提示,各端降级体验是否一致。
4)同城双活
机房故障往往是灾难性的,会大大降低用户体验甚至出现客诉,对用户和公司来说都是巨大损失。多机房failover能力显得至关重要,我们对直播核心业务场景实现了同城双活(首页、进房、送礼、预开播等),保证在机房失联、断电、失火等极限情况下,可以快速决策并切流,最大限度保证用户体验不受损。
吸取去年“B站713故障经验和教训”,SRE平台和基础架构团队研发了 Invoker多活切流平台。经过多次生产切流演练验证,单次切流平均时效5分钟内。大大提高了切流效率,避免故障影响面持续扩大。
5)网关迁移
直播第一代API Gateway是基于Envoy二次开发,部署在IDC物理机。资源预估出现偏差时临场扩容非常耗时,需要服务树挂载->审批->机器初始化->运行时初始化->灰度->接量→测试→SLB灰度→SLB全量,平均耗时30分钟+。
去年S11总决赛现场踩过这个坑,当时靠手速扩了20分钟才扩上8台,扩完之后一波突发流量差点儿炸了,CPU峰值90%了,好险!
同时Envoy网关C++编写、代码结构十分复杂、调试困难、很难维护,无法一键部署。今年我们将核心服务的BFF(Backend For Frontend)全部迁移到新的自研Golang API Gateway。主要有以下优势:
支持容器化部署
支持自动弹性伸缩HPA,分钟级别即可完成扩容!
具备快速便捷的控制面能力:限流、降级
HA:支持逻辑/物流集群隔离
支持全链路灰度
目前该项目已经开源,感兴趣可以学习:https://github.com/go-kratos/gateway
6、性能压测
经过前面的优化,我们初步保障了整个技术底座的抗故障能力,接下来会通过几轮周期性压测来验证核心业务场景的极限QPS能否达到要求,未达标的要分析出瓶颈并做技术优化/扩容。
1)压测目标
每年大型赛事活动结束后,我们都会对比赛期间的关键数据做存档,方便对明年目标值做出合理预估。依据去年业务数据(在线人数、营收数据)增长比例和核心接口峰值QPS,结合接口实际调用时机,很容易估算出今年接口预期QPS。
假设去年同时最高在线N,A接口峰值QPS=a(A接口进直播间必调一次、和在线人数线性相关),前面我们和上游业务方对齐业务数据目标(同时最高在线=M),则今年A接口预期要扛QPS = a *(1 +(M-N)/ N)
2)压测方案
通过抓包、代码翻阅整理出今年S12核心场景最新的接口依赖和调用时序关系(是串行还是并行),B站自研的压测平台Melloi支持对同一个场景的相关依赖接口进行编排。
今年的S12新增了很多用户玩法功能给用户带来沉浸式的观赛互动体验,涉及很多写接口的压测,可能会对生产环境造成数据污染,产生舆情和客诉。
B站在去年自研了全链路压测的方案,通过全链路压测标识Context传递,在数据写入层的SDK做拦截策略,将压测写流量转发到“Mirror数据隔离层”,实现压测数据隔离。压测结束后,压测平台联动数据库、缓存、消息队列等数据平台,快速回收数据。
S12送礼、心跳等直播核心写场景就采用了这个方案,通过对场景相关链路的上下游改造,借助于全链路压测平台真实摸底了数据库和异步JOB/Consumer的负载上限情况,设置了合理的限流,有效地保障了大型活动中写服务的稳定性。
3)压测执行
为了压测数据的真实性,我们选择低峰期在生产环境进行集中发压。虽然是在低峰期压测,但还是有一些正常用户流量,所以一定要注意避免压死整个系统:
开始要慢慢施压且压测时间短一些(比如1分钟),以防止出现问题。
紧盯各项监控(服务的监控,Redis,Mysql,Tidb,Databus等),如果有异常要立即停止压测。
资源使用逼近极限,需要停止压测。
记录压测过程中不同压测QPS下各项资源的压力情况,以供后续分析。
4)压测报告
压测后我们会系统整理压测报告:压测结果、目标Review、问题跟进等。
① 常见问题
② 扩容最佳实践
压测结束后,根据如下公式合理估算要扩容的副本数。
7、保障预案
保障预案主要分为两个方面:业务层和技术层。
1)业务预案
通过场景地图梳理、混沌工程、性能压测发现并治理了很多技术风险点,回看这些问题关键链路的技术优化,大多数链路降级调整依赖各种配置:服务配置、KV配置、上下游的配置。为了保障在整个S12进程中及时响应、关键链路不出问题,我们整理了各个场景中可能需要临场执行的各项预案。
我们针对15个S12核心场景做了保障预案,部分预案需要多个场景负责人进行联动、协同,会在线下线上做预案演练并验证有效。
2)技术预案
① 网关限流:结合前期压测的QPS,配置一个合理的限流QPS
② 服务Quota限流:支持按Zone、Caller进行限流
③ 网关降级:支持按PATH和Query/Header进行直接降级,不会将压力传递到业务服务BFF
④ 资源弹性
HPA:全称是Horizontal Pod Autoscaler,水平自动伸缩。当业务POD CPU/内存使用率超过设定阈值后,自动触发扩容,无需人工干预。
混合云:防止自建IDC机房资源用满,自动弹到第三方混合云资源上,充分保证了资源可用。
8、质量把控
S12八强赛后,我们启动了S12强管控升级。强管控期间,线上变更需要谨慎并报备到S12技术保障项目组进行Review把关。具体分为两个阶段:
9、现场保障
1)可观测性
往年的大型赛事保障活动,投屏的监控大盘无法直观发现系统问题,业务监控遍布在各个角落,排查问题非常不便。今年我们首先基于场景地图梳理出了S12 L0/L1场景的核心依赖:
接口
应用
数据库
缓存
消息队列
然后围绕业务核心指标PCU(最高在线用户数)、核心场景SLO、核心应用饱和度三个维度制作了新的健康监测监控大盘,1分钟自动更新一次。
① 业务核心指标PCU
直播大部分系统和PCU线性相关,一旦有波动要立即关注下游负载情况。
② 核心场景SLO
作为结果指标,对于微服务故障有着决定性的指导作用,出现波动一定是有问题了。对B站SLO体系建设感兴趣可以阅读:要是还没搞明白SLO,你算哪门子SRE呢?
③ S12核心应用饱和度
作为预警指标,可分为三个档位:
红:异常,需要立即介入处理
橙:偏高,需要关注
绿:健康,无需关注
2)告警协同
保障现场出现最多的问题就是告警了,我们目前的告警存在告警等级不明确、告警接受人不准确、处理状态不透明、告警风暴的问题,长期会基于SLO做告警治理。短期我们自研开发了一套告警应急协同平台,方便研发、SRE协同处理告警,一目了然。
支持场景管理
支持告警订阅:按告警ID
支持告警过滤:按告警ID、按告警权重
支持告警聚合:相同告警10分钟窗口内自动聚合
支持告警处理协同:告警处理流转状态一目了然,方便协同
3)现场值班
① 现场指挥官
问题优先级判断
应急响应
技术判断和决策
② 值班人员
业务:运营、审核
技术:研发
基础架构:SRE、DBA、网工
三、总结展望
今年是我加入B站的第5年,回想前几年S赛技术保障现场大家手忙脚乱地处理告警(扩容、限流、降级),现场非常混乱。即便是直播结束,告警和问题反馈也一直不断。今年我们通过一系列的技术升级(内核升级、去CPUSET、合池、网关容器化迁移、同城双活、HPA)和服务治理(混沌工程、全链路压测、告警协同治理),保障了整个S12直播过程中技术系统稳定、流畅,没有出现任何需要主动限流、降级、熔断等对用户有损的技术干预手段,给用户带来了极致的观赛和互动体验,实现了技术人梦寐以求的“喝茶保障”。后续我们会对技术保障过程中的各个环节进行复盘,持续打磨技术中间件和平台、建设多活单元化、全链路压测覆盖、优化资源池调度、全面推进B站基础设施云原生落地。
参考资料
https://www.infoq.cn/article/l3qnkhnyusv9w7tgxqvp
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721