全链路压测改造之全链自动化测试实践
本期作者
李思佳
bilibili资深测试开发工程师
2020年加入B站,深度参与全链路压测、多活、混沌工程等专项的建设和实践。
深耕系统稳定性测试领域。
01 背景与意义
B站直播营收送礼业务有着高写、在跨晚和S赛等大型活动下流量陡增、数据实时性要求高等特性,传统压测对于写场景为了避免影响线上数据做了各种屏蔽和黑名单处理,有着无法逼近线上真实情况的问题,因此业务对全链路压测有着较大的诉求,需要通过全链路压测来系统性地评估服务容量,发现瓶颈和隐患。
对于该诉求,直播技术侧结合公司的全链路压测方案,对营收送礼业务进行了全链路压测的服务改造。整个过程涉及到营收核心服务、底层中间件、压测sdk、压测控制台、施压平台等多处改造,链路长、配置多、影响的业务广,对质量方面的要求非常高,测试面临了比较大的挑战。一方面,要保障全链路压测安全可控,各个改造节点均需确保正确可靠,另一方面因底层改动带来的上层业务测试回归量巨大,测试效率难以保证。基于以上痛点,我们设计了全链自动化的测试方案,并与项目一起实践落地。接下来,我们来详细介绍下该方案的设计,希望能对一些正在或即将进行全链路压测建设的团队在质量保障工作上带来一些灵感和参考。
首先,我们先来介绍下业内普遍的全链路压测方案以及B站采用的方案,再基于具体的改造点来看测试范围。
1.1 行业内全链路压测方案对比
方案一:流量混布, 存储隔离, 线上施压
对线上服务压测,压测前根据容量预估和压测目标,对线上服务进行扩容和cpu、mem等相关配置的变更。
压测产生的数据与线上真实数据做隔离,采用影子库表的方式,防止污染线上真实存储。
需构造压测数据和压测流量,通过压测标记来区分流量属性。
方案二:对数据本身做标记, 逻辑隔离,线上施压
对线上服务施压,与方案一的区别在于数据隔离上,不是通过影子库表,而是在原表上增加标记,做逻辑隔离。
业务需要做适配工作来识别流量属性。
方案三:镜像环境 OR 线下压测
此方案在线下进行压测,部署线下测试环境或镜像环境。
线下环境稳定性不高,硬件资源和压测数据线上线下差异大,压测结果参考价值有限。
技术同学经过调研,基于当前业务的语言栈较统一、基础组件较统一以及服务治理较完善等特点,选择了方案一作为B站的全链路压测方案。
1.2 B站全链路压测方案介绍
B站的全链路压测方案在B站在全链路压测上的实践一文中有详细介绍,简单来说主要为流量混部、线上压测和存储隔离三个部分:
流量混部
- 与线上集群资源共用,在深夜低峰时期进行线上压测
- 通过流量打标的方式对流量进行区分,压测流量均带有压测标识,支持对http请求和grpc请求打标进行全链路压测
- 服务接入压测sdk,对压测流量进行识别、拦截和处理
线上压测
- 通过公司的压测平台,进行压测任务和场景设计、压测数据构造以及压测结果分析等,具体压测平台的设计及原理在B站压测实践一文中有详细介绍。
存储隔离
- 我们采用存储隔离的手段,对db创建影子表,redis创建影子key,mq创建影子topic,将压测流量完全隔离
- 搭建全链路压测配置控制台,管理压测规则,主要涉及对已接入全链路压测的服务的以下几点配置:
△ 需要压测的接口
△ 压测接口依赖的下游接口的透传/镜像/Mock规则
△ 数据库表的透传/镜像/写丢弃规则
△ 缓存的透传/镜像规则
△ 消息队列的透传/拦截/镜像规则
直播的全链路压测架构图如下,可以看到整体链路,由压测平台施压,被压测的服务接入压测sdk,获取到由压测规则控制台下发的压测配置信息,根据配置信息对接收到的压测流量做处理,如配置了镜像规则的数据表,压测数据写入影子表,对配置了镜像规则的redis,压测的缓存数据写入影子key等等。
针对此链路上如此多的服务改造点(SQL改造、redis改造、databus改造、job改造、context改造、go channel改造、sync/pipeline改造...),如何能又快又好又全面的测试覆盖,是我们设计全链自动化测试方案的初衷,我们将其主要分为三个阶段。
第一阶段,我们对各个新增节点分别做了测试保障,如mirror sdk、压测配置控制台等,保正底层基础能力的正确性。
第二阶段,当基础建设已完成,进入到了业务接入及全流程验证阶段。业务是不停迭代的,其中随着基架的不断演进,业务所涉及的服务也包含了部分历史债,当此套框架真正接入业务后,我们往往在业务实际使用中会发现很多不适配的地方,包括框架设计不够健壮或者业务的使用姿势不规范等原因,需要修复或兼容。这个阶段的测试也是最繁琐、测试量最大、重复性很高的地方,为此全链自动化测试全面应用于此阶段,来提升效率及业务覆盖度。
第三阶段,主要应对于未来的拓展,随着全链路压测覆盖的业务越来越多,当”常态化“的全链路压测计划提上日程,重复的工作和人力成本随之增加。此时测试工具更需要平台化及可视化,为压测前、压测中、压测后各个阶段的重复工作提供有效的自动化支持。
接下来,我们详细介绍在第一和第二阶段中,测试方案的设计及应用。
注释:
压测配置控制台:全链路压测配置中心,配置被测服务、被测接口、下游依赖接口、被测接口涉及到的缓存、数据库、消息队列。
mirror sdk:为大仓提供的压测控制sdk,通过配置文件可以直接控制数据库、缓存、消息队列等组件对压测流量进行处理。
1.3 全链自动化测试方案
我们主要遇到的测试难点如下:
改造均为核心业务,涉及改造的服务数多、接口数多,测试量大。
改造非常底层,涉及mysql、tidb、redis、databus等中间件,业务逻辑分支多,传统手动测试很难高效全面覆盖。
改造的服务涉及的依赖和配置多,中间任何节点的错误均可能导致在压测实施时影响到线上,如配置遗漏可能导致数据写到线上库、sdk故障可能导致压测标失效等,需要在压测前的进行“扫雷”。
设计方向思考
广度:借助接口自动化测试平台来保障业务的覆盖度
效率:人工校验转为工具自动校验,自动化校验业务逻辑
全链自动化方案设计主要包含以下三个部分:
链路分析
配置确认
自动化校验
02 主要测试过程与实施
2.1 链路梳理分析
链路梳理是保障全链路压测安全实施的前提,服务改造、压测配置、自动化校验等工作都强依赖链路梳理与分析,梳理工作非常繁琐,使用传统翻代码的手段不仅低效,而且容易遗漏而引发安全事故。这里我们主要采用动静结合的方式来完成链路梳理工作。
工具:
trace追踪:全链路跟踪系统,提供分布式环境下服务调用链监控,还原请求调用关系。
自动化代码规范扫描与检查工具bilicontextcheck lint:检查代码中不规范使用context的地方以及是否有context传递中断的场景。
静态扫描:调用链中容易出现因ctx使用不规范导致调用链断裂的情况,对此使用bilicontextcheck lint工具用来检查业务代码中ctx不规范的地方,确保调用链不会断,压测标识能完整传递。
链路追踪:依赖链路追踪工具可视化的返回服务链路的依赖关系,各节点对数据库、缓存、消息队列的调用信息等,以下是使用示例。
以上两个阶段,我们完成了大部分梳理工作,但由于业务实际代码逻辑的实现过于复杂和多样,为了保证梳理工作更全面且不遗漏,我们最后加上人工check代码的方式做补充,打通链路完整性梳理的“最后一公里”。
2.2 压测配置确认
通过对业务的链路分析,我们明确了具体的压测范围,业务的服务依赖关系,接着需要在压测控制台进行整个链路的压测规则配置,涉及服务的接口配置、依赖接口配置、数据库配置、缓存配置、消息队列配置,根据实际业务场景配置透传、镜像、写丢弃、Mock等规则。
2.3 自动化校验
测试覆盖率和效率的提升,均离不开自动化测试,因此,测试改造工作也基本围绕"自动化"来展开。在基架改造、业务服务改造、压测配置、服务部署等工作都完成后,将进入测试验证阶段。本阶段主要通过自动化的手段对业务进行正确性验证,两套流量均需包含以下内容的检查:
接口返回校验:response结果需符合业务预期,输入/输出参数检验、格式校验、容错处理、安全检验等
接口存储校验:mysql、tidb、redis的读写符合业务预期
业务异步流程校验:确认压测标识全链路透传,压测流量和正常流量在异步流程下trace均能正常关联,比如送礼链路的异步结算流程,在送礼接口调用完成后,可通过订单id查询结算表数据来验证结算是否异步调用成功
链路完备性校验:结合压测控制台规则,对接口的调用链路进行自动校验
自动化实施的关键key
自动化case需支持切换环境,支持正常流量和压测流量两套流量的构造、识别和检测
数据流能自动化检验,保证调用链路各节点符合预期,包括下游接口调用、数据库、缓存、消息队列等节点
要实现以上功能,我们需要对已有的自动化框架进行改造,保持原自动化case能复用的基础上,增加压测流量构造、链路完备性检测等功能,以下是我们团队自动化框架的分层设计图,红色部分为本次的框架改造点。
自动化框架分层设计:
通过上图,可以看到我们的自动化测试框架分为三层结构设计,最上层为case层,按业务做单接口用例和场景用例的编排,中间层为invoker层,做请求封装(http&grpc)、配置管理、断言、中间件连接等基础功能封装和聚合,最底层coverage层包含grpc和php服务测试覆盖率统计功能,本次改造在原有测试框架上进行,对框架的核心改造如下:
增加压测标识“mirror”,通过全局变量 config.mirror 控制流量入口,设计压测标识的从上到下透传。
在invoker层封装链路检测工具集“trace_toolset”,进行链路完备性检查。
在invoker层封装http/grpc请求,request header中增加压测标识mirror。
自动化框架改造 — 入口改造:
使框架能支持压测流量的构造,在流量构造层使用全局变量控制流量入口,能一键切换流量指向正常环境还是全链路压测环境。
改造点:在配置文件config中增加压测标识:config.mirror,默认值为空(若值为空则识别为正常流量)。
自动化框架改造 — 压测数据构造
压测写场景,往往涉及到要对压测的上游数据进行构造,如送礼场景,需要压测用户的钱包有余额,压测前需要在相关的影子表插入数据,这类型case属于压测前的数据准备工作,需识别并进行构造。
自动化框架改造 — 自动化链路完备性校验
链路完备:需要确保调用链路完整且染色标不能中断,保障方案分静态收敛和动态收敛两种策略,基于业务验证占据80%+工作量,此验证过程必须尽可能的自动化,为此我们引入链路检测工具集“trace_toolset”,自动进行链路完备性检查。
trace_toolset
基于接口链路的唯一标识traceid,从链路追踪工具中获取链路的各个节点,结合压测规则配置控制台的配置,依次检查调用链中的mysql、tidb、redis、databus的调用是否符合预期,分别对正常流量和压测流量两套规则进行检测,对于校验结果回传至上层case,输出测试报告,以下是链路检查工具与自动化框架结合下的调用流程。
自动化case改造复用
基于服务改造范围大、涉及的接口多、链路长的特性,怎么提升测试效率以及回归效率(底层bugfix回归)是需要解决的一大难题,本次方案主要采用case复用的思路来解决效率问题,复用已有业务沉淀的自动化case,在此基础上,保持case中间部分业务结构不变,通过mirror识别,仅修改头部流量入口和尾部规则校验方法,让case能复用于压测流量,快速将case翻倍。
03 案例实践
3.1 业务场景和服务依赖梳理
梳理压测接口的服务依赖大盘,以下是某场景的服务依赖关系图,通过服务依赖关系确认哪些服务需要接入压测:
根据深度遍历:定位到某个服务依赖其他服务的接口,对链路上的每个接口以及接口涉及的db、redis、databus做确认。
3.2 服务压测配置
基于梳理出来的链路关系在压测配置控制台进行配置,需根据实际业务场景配置透传、镜像、丢弃等规则。
3.3 业务自动化测试
正常流量自动化业务验证
压测流量自动化业务验证
业务逻辑、存储、trace完备性检查
3.4 问题排查与修复
对于运行失败的自动化case进行排查与修复,测试过程中主要可以发现以下4类问题:业务涉及的服务代码问题、配置问题、sdk&压测控制台问题、基架问题等。
3.5 预发验证,灰度部署上线
制定上线流程、进行风险评估和应急预案准备,实时监控上线过程, 确保业务安全无损上线。
3.6 线上全链路压测执行,压测结果分析
业务进行线上全链路压测,通过压测平台进行施压,发现潜在的业务风险。
3.7 全链路瓶颈分析与优化
对全链路压测过程中发现的异常点逐个进行排查与分析,探索系统瓶颈和隐患,确保业务服务的稳定性。
未完待续
对于全链自动化测试方案的第三阶段,面对未来可能出现的由业务”常态化“全链路压测带来的人力成本高、重复工作多等问题,我们的测试工具也在持续建设以贯穿整个压测生命周期,第三阶段的方案在后面的实践中我们再来为大家深度分享。