《英雄联盟》S11全球总决赛·决赛中国战队夺冠,赛事直播盛况空前,观赛人数破亿。B站作为英雄联盟2021全球总决赛直播独家版权方不仅在整个比赛过程中保证了直播整体总体运行的平稳,还抗住了超预期的流量。
如此大型的直播活动,B站究竟是如何保障系统稳定的呢?
以下内容整理自TakinTalks·live直播活动中B站吕帆老师的精彩分享,若想查看精彩回放戳👉https://news.shulie.io/?p=4279(文末附课件下载方式)
讲师介绍
吕帆
哔哩哔哩直播B&C端架构组leader
个人简介: 毕业于华中科技大学,硕士。曾就职于美团点评,蚂蚁金服等公司,拥有丰富的服务端研发开发架构经验,对于高可用高并发系统设计,系统稳定性保障,服务治理,单元化部署,网络协议,加解密,互联网常见业务(直播,营销,开发平台,交易等)等方面较多实践经验。
为了保障这场活动的稳定性,B站投入了近4个月的时间,上百人参与其中,从7月份就开始了相关筹备工作。总体规划包含Web端、移动端、服务端、运维、直播流、采购等多类型事务,同时涵盖了公司8个以上的团队、10余项大项执行项目。为了给到用户最佳的视听互动体验,反复打磨每一个使用场景、进行多轮压测、混沌测试、预设降级策略、限流策略、设定预案/SOP、临场保障等,从而保证该赛事直播的顺利进行。接下来我将从这里面挑一些重点来跟大家分享。
一、前置优化
前置优化有3块主要内容:服务规范化、制定指南针指标、日志的梳理与告警。
1.服务规范化
一共梳理了16条规范,其中有7条是需要大家重点关注的,可以简单分享一下其中几点内容。
服务之间的调用,服务调用存储层(Mysql、Redis)需要设置合理的超时,这个点没有经历过高并发高可用场景的话可能会不太理解。我们之前经历过这样一个事件,当时有个服务调用数据库,我们的超时设计不合理过长了,结果因为数据库抖动,我们整个服务都卡着等超时最后整个就挂掉了。这就是为什么我们会单独列这条规范了。
调用外部服务或者存储层如果重试的必要,需要有限的合理的重试,这也是从我们之前的经历中总结出来的规范。之前有个开发同学特别有心,想着Redis大部分是读比较多,为了让借口的性能提高,他做了一个重试,重试次数设置了5次。其实想法是好的,但遇到极端情况就出现问题了,当时这个Redis集群有一个节点抖动了于是产生了大量的重试,这个节点的流量一下增加了5倍,结果就打挂了。
2.制定指南针指标
指南针指标是服务对业务的核心价值体现,指南针指标同时也体现服务的健康与否 ,指南针指标的波动,一定意味着业务出现波动,业务出现问题,指南针指标一定异常。所以只要制定了正确的指南针指标,并加以监控和告警就能及时反馈系统问题。
3.日志的梳理与告警
出现问题大家的第一反应基本都是去看日志/监控,但就如同下图,有写告警它并没有告诉你影响这么样、发生了什么事情,那这就是无效的告警,无法帮助开发进行定位为题治标不治本。
为了让日志和告警发挥它原本的作用,我们对日志进行了分级并提出了规范要求,必须要体现报错的原因以及具体的解决方案。同时我们喧杂在可能出现故障的地方打日志设置告警,这样就能快速准确定位问题和解决了。
文末附课件下载方式
二、场景梳理
对于多数研发来说,关注的仅是自身服务是否可用,但是由服务串联成的场景才是实际用户感知的使用过程。定义场景能帮助我们更好的识别场景、理解场景、利于保障,是保障的基础。我们将场景的定义罗列了5项,其中场景级别、场景交互(包含上下游)、相关接口是比较重要的梳理项。
以B站的直播首页为例,大家可以看看我们针对一个具体的场景会梳理哪些内容。基于这一套梳理流程,我们一共梳理了40+的实际场景。
三、流量预估
系统的压力来自于海量用户每一个行为的流量。梳理好场景后,我们需要从入口梳理出每个场景的流量,也就是这个场景需要承受的QPS,然后再具体到这个场景涉及的每个接口的QPS,如进入直播房间场景:
从上图,我们总结出进房场景涉及的服务和接口以及对应的QPS。这里有必要说一下QPS的预估方法,主要有两种:
- 历史流量算法:根据当前&历年活动流量,计算整体业务体量同比增量模型。
此算法比较适用于一些老的场景,老的接口。
- 业务量-流量转化算法(GMV\DAU\订单量):一般以业务预估总量(GMV\DAU\订单量)为输入,根据日常业务量-流量转化模型(比如经典漏斗模型)换算得到对应子域业务体量评估。
此算法比较适用于一些新的场景,新的接口,特别是本次活动新增的。
四、压测
保障系统稳定性最大的难题在于容量规划,而容量规划最大的难题在于准确评估系统的实际承载能力。通过压测可以评估系统的实际承载能力。另外,高并发时,压测可以检测到一些在流量比较低的时候发现不了的问题。这次我们共进行了三轮压测,每轮压测都会执行多次:
- 第一轮:先不扩容,压出系统的极限,找出瓶颈和优化点
- 第二轮:等系统优化和业务需求做完,扩容后,按照S11预计流量压测
- 第三轮:验证,回归,查漏补缺
这里有总结一些压测的常见问题以及背后的原因,仅供参考。
文末附课件下载方式
经过多轮的压测,我们也总结出了压测时应该注意的事项:
压测前:
- 确定场景下所有要压测的接口,如果有的接口不太容易压测(比如会产生脏数据)暂时先不压测,但是后期需要做预案(比如如何保障,出了问题怎么办,降级方案是什么)
- 打开接口限流
- 压测信息周知到你依赖的服务的负责人,如果涉及Mysql,Tidb,Redis,MQ等同样需要周知到对应负责人
- 如果多个场景对同一个服务都依赖,那多个场景需要一起压测
压测中:
- 开始要缓慢施压且控制压测时间(比如1分钟),以防止出现问题。
- 紧盯各项监控(服务的监控,Redis,Mysql,Tidb,MQ等),如有异常,立即停止压测
- 遇到日志报错增多,立即停止压测
- 遇到接口耗时明显增加,立即停止压测
- 资源(服务器,Redis,Mysql,Tidb,Databus等)逼近极限,需停止压测
- 多轮压测,步步提升QPS
- 保证压测流量均衡
- 记录压测过程中不同压测QPS下各项资源(服务器,Redis,Mysql,Tidb,MQ等)压力情况,以供后续分析
压测后:
- 查看各项监控,服务是否健康,QPS是否正常,保证服务没有问题
- 如果有临时数据,是否要清除
五、混沌工程
虽然压测已经做过,但这只是在系统正常运行情况下的表现。真实情况中,线上系统经常会出现各式各样的异常,那么我们能否主动找出系统中的脆弱环节,提前做加固、防范呢?另外保障的核心还是在于人,所以我们还需要锻炼参与保障的同学,让他们能够临危不乱,冷静的解决遇到的问题,为此我们引入了混沌工程。 本次混沌实验的目的:通过故障注入,验收证明全链路的高可用性, 包括监控、告警、定位、恢复体系。主要聚焦在以下几个最常见的故障场景去做演练:
- DB不可用/不稳定(超时,抖动等)
- Cache集群不可用/不稳定(超时,抖动等)
- 依赖的RPC服务不可用/不稳定(超时,抖动等)
- 网络抖动
- 宕机
以下为本次我们的推进过程:
- 梳理整个系统的部署架构图,如下所示:
根据部署架构图,可以清楚地看到服务所依赖的各种资源的关系,这样就可以根据部署架构图在每一个调用链路上设置故障点。
- 设置故障点
根据部署架构图,可以设置DB不可用/不稳定,Cache集群不可用/不稳定,依赖的RPC服务不可用/不稳定,机器宕机,网络抖动等故障
- 故障演练
演练由测试同学在任意时间触发任意故障,然后记录开发同学可否1分钟发现故障,五分钟定位故障,十分钟解决故障。同时记录故障演练的过程,参考如下:
文末附课件下载方式
根据以往经验,故障演练可以分至少两轮,第一轮对所有故障点进行一场演练,演练完之后进行系统优化,第二轮可以采取”考试“模式对故障点进行抽检。
六、线上问题处理
前面介绍的都是如何保障系统不要出现问题,那万一真的出现问题了该怎么办呢?总结一下线上问题的处理手段一共有5种:限流、降级、熔断、扩容和Hotfix(改代码上线)。
1.预案与sop
要想在大型活动的高并发流量场景下,快速对线上紧急事故进行响应处理,仅仅依赖值班同学临场发挥是远远不够的。争分夺秒的情况下,无法给处理人员留有充足的策略思考空间,而错误的处理决策,往往会导致更为失控、严重的业务与系统影响。因此,要想在现场快速而正确地响应问题,值班同学需要做的是选择题,而不是陈述题。而选项的构成,便是我们的预案。保障S11顺利播出,我们对每个场景整理了一套处理问题的“标准作业程序”,也就是所谓的SOP。每一个SOP大致有下面四点:
- 我们需要确定每个场景的模块出现问题的表现;
- 确定问题出现了,我们要确认该怎么做;
- 谁来做;
- 怎么做。
2.降级
降级目的是削弱非核心服务资源占用,保证业务核心服务稳定性。降级策略有很多方面需要思考与落地,在这里总结一下经常用到降级策略。
- 页面降级:非核心页面模块,占用紧张资源,关停该页面,减少访问;
- 服务降级:将功能分类,分为核心服务与非核心服务,将非核心服务进行关停;
- 依赖降级:将依赖服务梳理分类,保证核心依赖的稳定,将非核心进行降级关停;
- 读写降级:将直接读写数据库切换为读写缓存;对于缓存依旧可以进行备份冗余;
- 延迟降级:页面的异步加载策略;数据写入异步消息队列等。
降级的实现方式,大体分为两种,一种是自动降级,一种是主动降级。
- 自动降级:
- 一般可以通过代码层面处理,比如调用接口返回了err,对于golang,我们可以判断err是否为nil,如果不为nil可以进行降级处理,如果是Java,出现Exception,可以在Catch中处理。
- 也可以通过熔断器进行自动降级,但是需要考虑好熔断的阈值。
- 手动降级:一般通过开关处理,开关的实现有很多种,比如配置中心。
了解了降级方法之后,我们还需要清楚不是所有服务都能降级,也不是等到故障发生了以后,才去选择或者确定哪些服务可以降级。故障的降级策略一定要提前规划与思考。 系统或者服务需要分为核心和非核心。核心服务是我们力保不能有任何问题的主流程服务 P0;非核心服务,又可以根据重要性进行再次分层。可以划分为 P1、P2、P3 服务:
- P0 服务为主服务,是力保稳定性的对象,他们挂了整个业务也就崩溃了;
- P1 服务为与紧密主服务相关,但可以后续异步补偿来操作的服务,比如说,结算流水,订单统计;
- P2 服务与主服务有点相关,但关闭了对主服务任何业务逻辑没有影响,比如说,订单评价,商品推荐等;
- P3 服务与主服务没有相关,关闭之后对主服务没有任何影响,比如说,广告推荐,用户喜好,评论浏览等。
在梳理服务等级的时候,需要注意 2-8 原则,也就是 20% 为核心系统,80% 为非核心系统。因此我们主要精力要放在20% 的核心系统。在本次S11稳定性保障中,我们做了一定的降级策略如:
- 直播首屏场景,正常情况下,会走算法推荐,但是如果算法推荐出了问题,通过开关切到简单排序规则
- 用户心跳场景,正常情况下上报间隔是一分钟上报一次,如果出现资源问题,可以在配置中心,修改上报间隔到5分钟
3.限流
大型微服务架构中的任何服务节点,不管怎么优化,怎么拓展,都会有能力上限。如果达到能力上限,系统服务崩溃的可能性就会增加,这种情况也很容易造成整个微服务应用的雪崩效应。 当服务器的压力剧增的情况下,为了保证服务不被拖垮,对一些流量采取拒绝或者降级的策略,以此来保证核心服务的正常运转。这是一种有损的技术手段。 因为限流是一种有损的技术手段,而且平时使用的情况不多,所以如何决策是否要做出限流操作,在S11的活动中我们做了如下定义: 大前提:流量的突然增加,或者依赖的资源(比如存储层)有异常(可能是流量突增导致也有可能是存储抖动导致)且对业务有损,短时间没有有效的恢复手段,系统无法承受当前流量,且认为限流可以缓解当前情况 举个例子:
- 存储资源(Mysql,Redis等)问题:比如Mysql等资源出现不可用情况(比如,cpu跑满,大量超时或者大量慢sql)且没有恢复迹象
- 流量突增:在某些情况下,你认为流量导致的问题,比如流量突增
- 外部接口耗时增加:如果系统调用外部接口,外部接口突然出现耗时增加,导致系统服务器的cpu跑满,导致系统不可用
限流不是目的,只是解决当前紧急情况的一个手段,并且对系统是有损的,所以要持续关注限流的效果和对系统的影响,并且及时解除。 本次S11,我们分别对核心接口做了:
- SLB限流配置
- Ekango限流配置
- WAF单IP限速
临场保障主要涉及值班人员安排和现场保障与事故记录两个方面,这边就不展开了。
写在最后
今年B站还会有S12赛季直播活动,所以我们现在也是陆陆续续开始相关的保障工作了。与之前相比,今年新增了多机房部署,这个也比较常见,就是某个机房出现问题了就可以快速把流量切换到另一个机房。B站的直播主要是读流量,所以我们做的是读流量的多机房部署,整体还是比较容易的。此外我们还在探索单元化和多活方向的保障手段,到时候可以再跟大家分享分享这部分的内容。
查看精彩回放戳👉https://news.shulie.io/?p=4279
直播Q&A整理https://news.shulie.io/?p=4312