其他
春节保卫战:腾讯百万 QPS 线上环境云压测方案解析
目录
1 背景与挑战2 解决方案2.1 压测模式选型 2.2 压测用例编写 2.3 测试数据构造 2.4 压测报表分析3 实践案例 3.1 手Q春保活动 3.2 视频业务容灾演练4 总结展望
01
背景与挑战
第一,验证新功能上线的吞吐量预期,保障系统稳定性,避免服务上线的流量瞬时击穿。 第二,老旧服务的重构改造。在降本增效的背景下,如何降低机器的部署成本以带来显著收益,是个值得思考的问题。老旧服务大部分存在业务流量构造的难题,大部分也不存在存量的用例自动化验证机制,会带来比较大的重构挑战。 第三,发现系统瓶颈缺陷以保障大型节点系统稳定性。春节、元旦、618、双11等大型活动预演,提前通过全链路压测发现系统瓶颈和缺陷,按照保障目标提前进行扩容、缩容。扩容是为了直接提高系统可处理的最大吞吐量,而缩容是为了验证该服务存在冗余的资源配额,在上游的处理能力跟不上的时候,该资源是浪费的。 第四,验证后台服务降级、弹性策略。部分业务在服务启动时存在资源预热加载、CPU使用率飙升、OOM等问题。这类问题大部分发生在业务流量比较大的情况下,平时不容易模拟,通过压测将流量线上放大能够有效的复现该场景。
现有系统大部分是微服务体系,存在上下游的链路依赖,第三方的链路不能直接压测(支付、云厂商服务)。很多情况下整个服务的瓶颈,不在当前压测服务。而直接采用mockserver 来模拟耗时和返回业务数据,也会隐藏该服务短板。
其次是流量构造失真问题。线上的用户量、关系链、请求参数的维度比较多,无法直接通过编写用例脚本(等价有限的参数构造逻辑)来模拟线上真实流量。固定化参数数据直接导致热点数据异常,也会导致压测失真,无法有效通过局部推算全局的表现。
此外,数据规模没有达到预期。大部分服务属于I/O密集型服务,业务瓶颈都在存储层服务,例如mysql、redis、kafka等中间件,是由于持续量变导致的质变。数据规模在翻番的情况下,上下游链路的耗时表现可能呈现出雪崩效应。
02
解决方案
通过全链路压测精准评估系统上下游服务容量进行扩容、缩容,减少机器部署配额; 通过压测将性能测试左移,提前定位和分析性能瓶颈,保障服务稳定性。
2.1 压测模式选型
处在线性增长区时,响应时间(RT)基本稳定,吞吐量(RPS)随着并发用户数(VU)的增加而增加。 三者关系符合Little定律:VU=RPS*RT。随着VU增大、系统的资源利用率饱和,系统到达拐点。 若继续增大VU,响应时间开始增大,RPS开始下降。继续增加VU,系统超负荷、进入过饱和区,此时响应时间急剧增大、RPS急剧下降。
场景一:接口耗时敏感,并发量小,时延失真
场景二:压测机&被压服务资源充裕,RPS达不到预期目标
2.2 压测用例编写
// Send a http get request
import http from 'pts/http'; // 协议适配模块
import { check, sleep } from 'pts'; // 常规编排流程封装
export default function () {
// simple get request
const resp1 = http.get('http://httpbin.org/get'); // 执行发包操作
console.log(resp1.body); // 日志打印用于问题定位
// if resp1.body is a json string, resp1.json() transfer json format body to a json object
console.log(resp1.json());
check('status is 200', () => resp1.statusCode === 200); // 通过断言集成业务指标
}
// Init 从用户脚本注入底层实现,包括加载 metrics 上报插件&注入 otel 实现框架。
var Init = plugin.Init
// Run 核心脚本编写逻辑,引擎会按照压测模型执行该 Run 函数。
func Run(ctx context.Context) error {
// 必须通过 NewRequestWithContext 传入 ctx 构造请求,否则无法展示 har 格式的采样数据
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpbin.org/get", nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 自定义断言,自动上报业务指标
assert.True(ctx, "status code is 200", func() bool {
return resp.StatusCode == http.StatusOK
})
return nil
}
支持插桩代码引用,无需额外上传协议文件,减少频繁数据序列化操作带来的性能损耗; 对后台开发者友好,基于go进行用例编写,可以更灵活地复杂场景编排,支持现有存量协议封装、工具库集成,提高代码复用效率; 平台集成 HTTP、gRPC等协议指标采集,用户聚焦在用例编排,请求流量构造等场景,减少用户心智负担。
2.3 测试数据构造
2.4 压测报表分析
合理选用gauge、counter、histogram(分bucket) 优化指标聚合效率; log配合trace设置采样,保证脚本执行日志链路完整; trace&log默认设置合理采样比例,减少日志上报带来性能损耗。
03
实践案例
在6-8倍的日常流量保障目标下,探测上下游的服务过载情况,提前进行扩容尽早干预; 针对链路超时的现象,合理设置重试策略、超时时间,验证柔性策略是否生效,避免瞬时压力造成雪崩效应; 支持上海、南京、广州等多个地域集群压测,最高达到10w并发数,目标RPS达到100w的吞吐量规模,支持100G级别带宽流量验证。
容灾演习:为了验证首页接口在各种异常情况下的容灾容错能力,梳理容灾容错短板; 压测:为了排查首页链路中的各个关键服务性能是否存在问题,找到链路性能瓶颈,明确链路服务扩容模型; 接入层兜底能力摸底,当首页接口故障情况下,兜底能力能否达到预期的目标。
混沌工程注入,验证接口的健壮性; 兜底缓存策略的触发机制; 验证服务降级、熔断策略的机制; 验证服务的过载保护能力,具备柔性可用; 校验业务安全管控策略、高可用。
验证业务的弹性伸缩能力,对降级、熔断、柔性服务进行可用性验证; 通过 PTS 提供的 RPS 扩散模型,了解上下游服务的机器规格配比,为容量预估、机器扩容、缩容做好评估依据。
04
总结展望