在2022年,阿里出品了一本《阿里测试之道》,讲述在阿里电商、物流、金融、大数据等业务场景下,测试活动业务层面是如何开展,以及技术层面是如何实现的。笔者有幸拜读此书,从其中也有所收获。本次以其中涉及电商的测试场景为例,记录一下“大促质量保障”一章的读书笔记。这一章主要讲述了在大促场景下,全链路压测是如何实施的,以及通过什么方式保证最终大促时的生产环境稳定性。
风险与挑战
全链路压测目标:尽可能真实模拟流量洪峰,进行高可用验证
用例场景需要考虑两个方面:
- 横向多样膨胀:域内行业通用、特殊玩法叠加
- 交易红包玩法,不同玩法红包叠加,核销打款失败
- 汽车行业轮胎+服务进到购物车,同店非轮胎商品进入后,购物车无法撤单
- 纵向长链路顺畅:完整旅程顺畅
- 一种表达货品重要销售类型的核心数据,由库存中心的系统打在交易订单信息上,通过履约系统传递,在分销系统、物流订单系统层层传递。在预售/相对时间销售情况下有问题,这个场景下数据需要变更,但库存是不管预售尾款的,不会更新这个数据,导致数据传递错误。
- 后果是消费者预约到了早于货品到仓的时间,商家无法履约
- 一种表达货品重要销售类型的核心数据,由库存中心的系统打在交易订单信息上,通过履约系统传递,在分销系统、物流订单系统层层传递。在预售/相对时间销售情况下有问题,这个场景下数据需要变更,但库存是不管预售尾款的,不会更新这个数据,导致数据传递错误。
全链路压测
影子体系
- 流量隔离:压测流量有特殊标识,打在流量入口url上
- 全链路贯通:压测标识在Web服务器的LogFilter映射到中间件的context,从而透传到整条链路
- 资源共用:影子体系资源共用线上资源,影子表、正式表在一套数据库中,缓存中通过不同key区分压测和现上数据
压测模型
- 入口集合:覆盖商品详情、购物车浏览、确认订单、提交订单、订单列表、订单详情、二次付款、付款成功等入口
- 包含各个后端模型(不是前端流量入口的模型)
- 预测出各个场景QPS和各个后端元素比例
- 和大促玩法深度结合,用大促玩法对压测模型修正
- 构造压测模型
- 在生产环境产出和双11同等量级数据:包含买家、卖家、商品
- 根据模型各个元素,完成构造优惠、领取红包、添加购物车操作
- 根据各流量入口请求规则,构造可行的压测流量
执行策略
- 千万级施压能力
- 一个流量平台,包含一个控制节点和上千个worker节点,每个worker节点部署压测引擎
- 压测引擎部署在cdn上,从外网向内网施压,模拟真实用户行为
- 执行方法
- 脉冲压测:按照设定模型脉冲,模拟0点流量
- 摸高:关闭限流,直接加压,直到扛不住
- 限流验证:摸高稳定情况下,开限流,看能否生效
- 破坏性测试:保持系统大促态,压力保持在峰值,执行各个紧急预案,看效果是否满足预期
常态化智能压测:非大促态下,通过智能压测机器人,对系统进行固定频率压测,及时发现系统瓶颈,定位原因
全链路功能
功能测试方法:提前将线上业务数据同步至影子表中,将机器时间修改到大促时间点,模拟真实行为,然后执行业务链路,验证数据可用性准确性
统一环境隔离
- 流量隔离:http前端入口、rpc调用、消息入口、定时任务
- 时间控制
- 修改JVM参数,只影响当前Java应用,阿里JVM有控制时间偏移参数
- 直接用date命令改虚拟机时间会影响机器上的其他应用
- 预案开关只在隔离环境生效,测试时不影响线上发布环境情况
全链路影子数据
- 数据生成:线上数据脱敏转换+数据区间映射,生成数据并写入影子表
- 特点:真实、海量、准备成本低
用例精简
步骤如下:
- 用一个RBC agent部署在交易链路核心应用上,用RBC服务端收集agent的数据
- 将每个应用中用例请求经过的代码信息采集下来
- 服务端接受代码覆盖信息后,将用例请求转化为链路级代码覆盖路径
- 将覆盖代码路径转换为唯一的内容签名,进行聚合
- 聚合后,进行数据固化
注:相当于通过调用链路来确定用例类型,由线上数据产生用例
RBC服务
- 代码覆盖计算任务全部都在RBC服务完成,进行离线计算,不在agent计算
- 用metaq代替tlog进行数据通信,metaq只上传存储地址,存储探针数据在服务端,压缩存储,减少metaq和OSS开销
- 服务器从metaq接到消息,从OSS拉取原始数据,计算链路覆盖信息,并改造jacoco行覆盖率算法提升效率
- 通过阿里鹰眼监控将用例在交易链接各个应用串联起来,提升覆盖率工具到链路级
- 用hbase替代mysql存储覆盖率信息
注:笔者以前做过skynet服务lua覆盖率测试,有些措施类似(比如代码覆盖在中心服计算),引入消息队列、OSS等,可以做更灵活的扩展(覆盖数据收集分析不需要即时性),中心服弄成集群也可以,覆盖数据分析计算可以再用非RBC中心服的其他节点来做(优化点)
全民预演
风险:
- 测试链路协同成本高:交易链路上下游关联紧密复杂,优惠应用调整可能涉及上游导购、投放,下游交易、支付,也包括售后的服务、履约、供应链等方面
- 迭代量大:设计几十个事业部,万级发布,单链路无法满足全局质量保障需求
- 提前保障:大促核心业务玩法来之前,不会在线上生效
目标:
- 提前全链路验证业务购物功能正确性和完整性:重点关注用户在购物链路上遇到的营销、资金、物流、服务、氛围心智、价格表达等交易/导购链路上功能
- 大促产品验收:产品设计&实现的匹配度、视觉交互
- 探索测试
预演实施:
- 人员构成:测试+研发所有人员
- 业务范围:交易+导购链路
- 交易:商品详情到逆向交易的链路上,价格、库存、用户限购、资金、资产、物流、服务等核心交易信息的披露和计算
- 导购:基础导购、导购产品、推荐链路上的营销表达和价格透出
- 用例:核心业务场景、用户行为路径
- 环境:网络上区分内网环境、外网隔离环境、wifi、4G;部署区分多地多单元、云与非云环境
执行策略:1轮专项+2轮全面+2轮验收
问题跟进:链路上核心测试/研发支持bug筛选归并,每轮预演发现问题通晒
CI:全民预演中的用例沉淀成接口,UI层面用例高频执行
平台化:提升全民预演效率
预案开关
分为两种:
- 提前预案:也是定时预案,提前预估大促期间系统业务状况,避免大促的业务峰值影响进行的缓存预热、机器重启、有限降级、磁盘清理或者业务下线,对业务没有明显影响
- 应急预案:针对可能存在的异常(流量超标、服务不可用),对业务有损,需要技术层面兜底
使用场景
- 大促预案
- 事前:提前预案,设置固定执行恢复时间(热点商品预热;提前对外公告/定时上线等业务操作;降级日志等非核心功能;降级无法承受大流量的下游服务调用)
- 事中:应急预案,根据系统业务指标决策(功能、服务降级/下线)
- 事后:回补,或者将功能大促态切换为日常态(数据追回)
- 日常预案:线上故障快速回复,建立风险关联提前预案的关系
评估方式
- 应用强弱依赖关系明确,强依赖的外部服务都可以纳为预案对象
- 预估流量、压测验证,确定外部服务是否要提前降级
- 预案开启大概率有损,需要做好用户引导
预案流程
- 活动前:预案梳理、录入、正确性检查、演练、时间检查、通知配置
- 活动中:执行检查/结果验证、紧急预案执行与补偿
- 活动后:恢复检查/结果验证、紧急预案恢复检查
平台化
秩序化进行所有预案操作,降低多平台、多角色操作带来的风险,提升预案链路沟通效率
目标
高可靠、高安全、低成本
全链路预热
混部技术(类似潮汐车道,同一个节点的资源在多个集群内用到)降低机器成本,但是快上的应用导致混部机房没有足够时间通过线上用户访问充分预热,导致在峰值时刻比非混部机房更容易产生瞬间流量支持不足问题
- 系统预热:用较小的压测流量对新发布系统运行20min左右
- 数据预热:将核心系统热点数据提前放入缓存中,防止系统发生缓存穿透、缓存击穿问题,主动防御
- 场景编排调度:先预热底层热点数据,再预热上层数据;考虑一些特定时间后才能执行预热的应用
适用的业务场景:
- tair预热
- 本地缓存预热
- db的buffer pool预热
- 缓存文件分发预热(本地加载)
- 缓存失效
快速扩/缩容
分为水平伸缩(增加、减少实例数,时间长)、垂直伸缩(改变硬件规格,秒级,限于宿主机资源)
优化方法:
- 水平伸缩
- 扩容步骤精简
- 紧急扩容不需要审批
- 扩容锁可以不用,因为不需要考虑应用发布版本不一致问题
- 账号、通道,可以异步添加
- 主机用途不需要修改,不需要加主机白名单等业务
- agent不需要检查,成功率可以保证
- vip操作可精简,比较麻烦
- 新技术架构:DADI
- 时间瓶颈:普通docker容器启动慢,需要在启动之前把每层镜像都下到本地,再解压到docker指定目录中
- 优化:zroot预热docker-registry数据,下发到节点的zagent
- 只需要下载容器启动必要数据,来加速docker启动过程
- 启动容器只下载layer_meta元数据,容器启动需要什么数据,就从zroot读取
- 扩容步骤精简
- 垂直伸缩
- 用cgroup实现资源分配
- 需要有资源预留
风控识别引擎压测
包括黑产、直播文本、商品都有风控需求
需要解决的问题:
- 风控压测模型和交易压测模型有冲突,数据构造不兼容
- 风控对数据强依赖,交易压测模型和防控数据不同意,两份数据存储成本大,不能实现隔离
- 风控防控规则变化多,影响性能
风控规则是由表达式+逻辑运算组成,分层,有短路执行特性,规则之间无关联,可以配置流转策略(命中继续/任何情况继续),因此影响性能的因素包括:
- 单个表达式性能消耗:基本是定值
- 表达式的执行结果:影响规则执行表达式条数,影响流入下一条规则请求数
如何通过控制表达式结果,让流量规则符合预期,可以:
- 将表达式结果true/false比例作为预期结果
- 执行过程中改写表达式执行结果或者通过数据构造,将表达式结果符合预期
第一点可以保存回流的风控数据,对表达式执行结果的比例进行特征挖掘提取
第二点数据构造,需要改写表达式,满足条件如下:
- 只对压测流量干预,真实流量不处理
- 干预生效不发布,对应用无感
只干预压测流量的实现,只要在runtime对压测标准进行识别,因为实现应用无感干预比较麻烦,最终采用javaagent挂载,对接口方法以字节码增强的方式干预(jvm-sandbox也可)
注:干预,实际上就是热更逻辑实现,参考文章:https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html
最终,可以基于历史数据学习大促期间表达式执行特征,抽象成tuple:(规则ID,表达式ID,结果为true比例),使用jvm-sandbox编写module可以实现表达式改写,从而影响流入每条规则的请求,实现压力模型和大促期间保持一致。方案上,基于表达式特征(三元组)和规则特征(二元组)根据不同场景写入配置中间件,压测人员在控制台操作Agent挂载,在压测流量流入时,可以根据配置的特征对表达式、执行结果进行干预。