专栏文章 性能测试 - 字节跳动全链路压测方案实践

大话性能 · 2024年01月04日 · 最后由 大话性能 回复于 2024年01月04日 · 10996 次阅读

给大家分享一篇关注压测的干货,针对字节企业全链路压测实践分享!很多大厂都需要,希望能给你的工作带来一些借鉴。
文章很长,需耐心阅读。

本文目录

  • 全链路压力测试的宏观介绍
  • 全链路压力测试的工业级案例
  • 1、字节跳动全链路压测系统背景
  • 2、解决方案
    • 2.1 业内实践
    • 2.2 架构图
  • 3、核心功能介绍
    • 3.1 数据构造
    • 3.2 压测隔离
    • 3.3 链路治理
    • 3.4 发压模式
    • 3.5 压测熔断
    • 3.6 任务模型
    • 3.7 压测引擎
    • 3.8 压测监控
  • 4、压测实践
    • 4.1 重大项目支撑
    • 4.2 日常压测任务支撑
    • 4.3 线上流量调度
    • 4.4 常态化压测
    • 4.5 DevOps 流水线中的压测
  • 5、总结与展望
    • 5.1 总结
    • 5.2 未来发展

全链路压力测试的宏观介绍

全链路压测是在模拟实际用户使用场景的基础上,通过对整个应用程序栈进行压力测试,评估系统在高负载下的表现。这包括了从用户界面、前端服务、中间件到后端数据库等所有组成部分,以全面了解整个软件系统的性能。

什么是全链路压测

全链路压测是一种系统性的性能测试方法,旨在模拟真实用户场景下的完整操作流程,全面评估软件系统在不同压力下的性能表现。这种测试方法对于保证应用程序的高可用性、稳定性和可扩展性至关重要。
基于实际的生产业务场景、系统环境,基于真实数据模拟海量的用户请求对整个业务链进行压力测试,并持续调优的过程;

全链路的核心为:业务场景、数据链路、压力模型和环境拓扑;

全链路压测不仅仅是一种测试手段,更确切来说其是一种测试过程,该过程涉及自动化测试/性能测试/高可用测试技术以外,还覆盖性能分析调优以及扩缩容解决方案等等。
更多内容可以学习《测试工程师 Python 工具开发实战》书籍《大话性能测试 JMeter 实战》书籍

全链路压测的测试流程

全链路压测的测试流程通常包括以下步骤:

  • 场景设计:根据实际用户使用情况设计测试场景,考虑不同用户操作、访问路径和数据负载。

  • 脚本录制/流量拷贝:开发测试脚本,录制用户在系统中的实际操作,包括页面浏览、交互和数据输入等。或者通过组件,在请求链路上拷贝一些流量,清洗后放到 HBase、MongoDB 这些 NoSQL 存储组件,称之为流量数据工厂。

  • 负载生成:利用自动化测试工具或性能测试工具,模拟多用户同时访问系统,逐步增加负载,直至系统达到极限性能。

  • 性能监测:在测试过程中,监测系统的各项性能指标,包括响应时间、吞吐量、资源利用率等。

  • 问题分析:分析测试过程中发现的性能问题,可能涉及到系统的瓶颈、资源耗尽等方面。

  • 优化建议:基于测试结果提出性能优化的建议,可能包括代码优化、系统配置调整或者硬件升级等。

在进行流量拷贝时,有很多的可选工具,比如 goreplay。goreplay 是一个开源工具,抓取生产环境流量,并使用真实流量持续测试系统。

goreplay
它使用 raw-socket 抓取系统流量,并根据指定的 url ,将流量进行转发。
GoReplay 劫持流量,拷贝到流量工厂。
要对流量进行染色,比如在请求头标记是压测流量
当需要压测的时候,从这些工厂获取数据,将数据切分成多分下发到测试节点。
压测的时候,压测流量不会一次加完,缓慢加,跑跑,看看监控,有瓶颈,回退,扩容,再加。

正式流量和压测流量隔离如何隔离?

压测流量拷贝下来的同时,我们也需要改造系统,实现将正式流量和压测流量隔离。
压测,有些行为影响到用户的统计分析,推荐等,还有一些插入的操作。这些就需要特殊的处理
MySQL 放入影子库、Redis 设置不同的前缀,es 放到单独索引表中。
写操作是上行流量,读操作是下行流量,都要有特殊处理

全链路压力测试的工业级案例

这里,尼恩找到一个漂亮的生产级案例:《字节跳动全链路压测 (Rhino) 的实践》。
这又一个非常 牛逼的工业级、生产级全链路压测案例。
注意,这些案例,并不是尼恩的原创,仅仅是尼恩在备课的过程中,在互联网查找资料的时候,收集起来的,供大家学习和交流使用。
特别声明,学习材料来自互联网, 不做商业使用,尼恩也仅仅作为学习材料使用。
由于没有原作者的联系方式,这里没有原作者的授权,如果原作者不同意我的发布, 给我留言就行,我即刻从《技术自由圈》公众号扯下来。

1、字节跳动全链路压测系统背景

作者:廖尧,字节跳动技术团队
随着企业的持续发展,业务范围的拓宽带来了用户访问量的显著增长,这进一步导致了研发体系的规模扩大和复杂性提升。同时,线上服务的稳定性变得尤为关键,服务性能和资源容量的问题也更加突出。
面对这样的挑战,建立一个高效的压测系统变得迫切需要,它应能确保线上服务的稳定运行,提供安全、高效、真实的全面链路压测服务,从而为线上服务的持续稳定提供保障。
关于构建全面链路的压测系统,虽然业内已有众多讨论,但在技术实现细节上却鲜有深入阐述。本文旨在通过概述从设计到实施的全链路压测系统的整个实践过程,详细阐述该系统的具体设计方法和实施步骤,希望能够从技术实践的角度为同行业的专业人士提供有益的参考和启示。

2、解决方案

2.1 业内实践

全链路压测在业内已经有了广泛的实践,如阿里的 Amazon、PTS [1] [2],美团的 Quake [3] [4],京东的的 ForceBOT [5],高德的 TestPG [6] 等等,它们的实践经验和技术方案为我们提供了宝贵的借鉴。我们综合了各大互联网公司的建设经验,并结合字节跳动的具体业务需求,设计并开发了一套全链路压测系统——Rhino。

2.2 架构图

Rhino 平台作为公司级的全链路压测平台,它的目标是服务于公司所有业务,提供安全、可靠、真实、高效的单服务及全链路压测。这有助于业务部门高效、便捷地完成性能测试任务,并更精确地评估线上服务的性能和容量风险。
因此在 Rhino 平台设计之初,我们就定下以下目标:

  • 安全:由于所有压测均在线完成,理论上都会对线上用户产生一定影响。因此,压测平台需从服务状态和压测数据两方面确保安全性。

  • 高效:较少压测脚本编写成本,数据构造和压测监控成本,尽量自动化完成压测过程的各个阶段。

  • 准确:精确的压力控制,准确的链路压测监控,精确的压测报告结果,以及性能和容量数据。

  • 高覆盖:需要支撑公司内不同的业务线的压测需求,如搜索,广告,电商,教育,游戏等等。

Rhino 是一个分布式全链路压测系统,可以通过水平扩展,来实现模拟海量用户真实的业务操作场景,对线上各种业务进行全方位的性能测试。它主要分为控制中心 (Rhino Master) 模块,压测链路服务模块,监控系统模块,压测引擎模块,如图。(每一个模块都是由多个微服务来完成的。如下图每个实线图都代表一个微服务或多个微服务)。

3、核心功能介绍

构建全面的链路压测平台,其核心要素包括:数据生成、测试环境的隔离、链路管理、任务分配、压测的断路器、测试引擎和监控系统。接下来,本文将逐一解析在 Rhino 平台上如何精细地设计和实施这些功能。

3.1 数据构造

首先,数据生成是压测中至关重要的一环,也是最为复杂的环节。压测数据的建模,直接影响了压测结果的准确性。
对于服务性能缺陷扫描,性能调优,以及新上线服务,推荐构造 Fake 数据,来压测指定路径。
对于线上容量规划,性能能力验证,以及性能 Diff,推荐使用线上真实流量,使压测结果更贴近真实情况。
对于涉及到用户账号,用户登录态保持的情况,推荐使用压测专属测试账号,避免影响线上真实用户。
基础数据构造
为了高效的构造特定的 Fake 压测数据,Rhino 压测平台提供大量数据构造方式:
CSV 文件:据按列分割,字段名取自 CSV 文件的首行。读取方式是逐行递增。若一个测试任务被分解为多个子任务(Job),相应的数据文件也会被分割,以防止数据在不同 Job 间发生重复。

自增:变量类型均为数字类型。每次发压时 +1,到最大值后从最小值循环使用。

随机:变量类型均为数字类型。每次发压时随机生成。

常量:Constant,可自定义为任意值。

压测账号
在压测的执行过程中,经常需要模拟用户登录并维持会话状态,同时许多测试请求还涉及到用户的唯一标识信息,如 UserID、DeviceID 等。如何处理这些测试数据,尤其是在保持用户状态和数据隔离方面,一直是一个挑战。Rhino 平台通过其集成的用户中心服务,提供了一套专为压测设计的账号服务,有效地解决了登录状态和测试账号的问题。具体流程和使用界面,如下图。

3.2 压测隔离

压测隔离中需要解决的压测流量隔离,以及压测数据的隔离。
压测流量隔离,主要是通过构建压测环境来解决,如线下压测环境,或者通过泳道化(Swimlane)和 Set 化(Set-based)的方法来实现。这样做的好处是测试流量不会影响到线上用户,但缺点是它需要更多的机器资源和更高的维护成本,并且测试结果可能需要经过换算才能准确反映线上环境的容量,从而影响结果的准确性。目前,公司在线上集群上进行压测,同时也在积极推进线上环境的泳道化建设。
压测数据隔离,主要是通过对压测流量进行染色,让线上服务能识别哪些是压测流量,哪些是正常流量,从而对测试流量进行特殊处理,以达到数据隔离的目的。目前 Rhino 平台整体压测隔离框架如图。

压测标记
压测标记就是最常见的压测流量染色的方式。
对于 RPC 协议,会在请求的头部中增加一个 Key:Value 的字段作为压测标记。
对于 HTTP 协议以及其他协议的测试,可以在请求头中自动注入一个特殊的 Stress 标记(Key-Value 对)。
压测标记 Key:Value,其中 key 是固定的 Stress_Tag 值,但是每个压测任务都有唯一的 Stress_Value 值,主要用于解决压测数据冲突,以及性能问题定位。
压测标记透传
目前,公司内的各个基础组件、存储组件以及 RPC 框架都已支持压测标识的透传功能。透传的原理是将标识的 Key-Value 对存储在请求的 Context 中,并在所有的下游请求中携带这个 Context。这样,下游服务就可以根据 Context 中的标识来处理测试流量。在实际的业务代码中,实现起来也非常简单,只需要传递 Context 即可。
Golang 服务:将压测标记写入 Context 中。

Python 服务:利用 threading.local() 存储线程 Context。

Java 服务:利用 ThreadLocal 存储线程 Context。

压测开关
为了解决线上压测安全问题,我们还引入了压测开关组件。
每个服务每个集群,都有一个压测开关。只有打开压测开关时,压测流量才能流入到服务内,否则就会被底层微服务框架直接拒绝,业务层无感。
在每个 IDC 区域,都设有一个全局的测试控制开关。只有当这个全局开关处于开启状态时,测试流量才可以在该 IDC 内进行流转。
当线上出现压测问题,除了从源头关闭压测流量以外,关闭目标服务的压测开关,也能立即阻断压测流量。
压测数据隔离
线上压测中,最复杂的问题就是压测链路中涉及到写操作,如何避免污染线上数据,并且能保证压测请求保持和线上相同的请求路径。业界有很多解决方案,常见的有影子表,影子库,以及数据偏移,如图 [7]。
Rhino 平台针对不同存储,有不同的解决方案:
MySQL、MongoDB:对于 MySQL、MongoDB 等数据库,采用影子表的策略。SDK 会判断流量是否为测试流量,如果是,则根据配置将操作映射到影子表中。配置策略包括读写影子表和读线上表写影子表两种。

Redis:Redis Key 加上 Stress 前缀。如 Stress_Tag=Valuex,那么读写 Redis 的 Key=Valuex_Key。这样可以解决多个压测任务数据冲突的问题。压测结束后,只需要对 Prefix=Valuex 做清除或过期操作即可。

MQ:对于消息队列,Rhino 平台有两种策略。一是直接丢弃,然后针对消息队列的性能,单独进行压测;二是在 Header 中透传压测标记,Consumer 根据压测标记和业务需求,再做特殊处理。默认走丢弃策略,业务方可根据需求进行配置。

其他存储,如 ES,ClickHouse 等,都有压测集群。压测时,会将压测请求打到指定的压测集群中。

服务压测改造
在压测之前,需要对服务进行压测验证。对于不满足压测要求 (即压测数据隔离) 的服务,需要进行压测改造。
压测验证:对于存储服务,在没有开启测试控制开关的情况下,任何读写操作都会被拒绝。如果没有遭到拒绝,说明在操作存储服务时没有携带测试 Context,需要进行改造。

压测改造:压测改造是线上全链路压测推进中非常关键,而又非常困难的一个环节。对于已经上线的服务,压测改造还极有可能会引入新的 BUG,所以经常推动起来比较困难。因此为了解决这些问题,Rhino 平台有以下几个解决方案:

尽量减少代码改动,并提供完整的指导手册和代码示例,以减少开发人员的工作量并降低代码错误的风险。

提供简单易用的线上和线下 HTTP&RPC 的测试请求调试工具,以便于开发人员进行代码改造的验证。

对于新项目,在项目初期就将测试改造纳入开发规范中,以减少后续的代码改动。

3.3 链路治理

链路梳理
请求调用链,对于线上压测是非常重要的:
提供清晰压测流量地图,并提供完整的链路监控。

完成服务依赖的梳理,检测压测所依赖的服务/中台是否具备压测的条件,是否需要压测改造。

链接压测开关管理,压测上下游周知等。

Rhino 平台通过公司的流式日志系统来完成调用链检索的。一个服务在被请求或者请求下游时,都会透传一个 LogID。RPC 框架会打印调用链日志(包括 RPC 日志 - 调用者日志,Access 日志 - 被调用者日志),所有日志中都会包含这个 LogID。通过 LogID 将一个请求所经过的所有服务日志串起来,就完成调用链检索。

Rhino 平台在公司流式日志系统提供的链路梳理功能基础上,进行了进一步优化,以满足压测需要:
自动梳理:鉴于公司采用微服务架构,每个请求背后的调用链非常复杂,人工维护几乎是不可能的任务。用户只需提供请求中的 LogID,Rhino 平台就能迅速地梳理出该请求经过的服务节点,如图所示。

实时梳理:由于线上服务不断在变化,上线下线新增等,因此同一个请求的调用链也是不断变化的。Rhino 平台建议一般使用 1 个小时内的 LogID 进行梳理。

多调链路合并:对于同一个接口,不同参数下的调用链可能各不相同。Rhino 平台能够将多个 LogID 梳理的结果自动合并,以补全调用链,确保链路梳理结果的准确性和完整性。

压测周知
虽然 Rhino 平台对于压测有很多的安全保障措施,但是对于大型压测,保证信息的通畅流通也是非常重要的。因此在压测周知方面,Rhino 平台也提供了很多解决方案:
一键拉群:在完成链路梳理之后,在压测前可以一键拉群,将链路中上下游服务的 Owner 拉到同一个群里,以便及时同步压测信息。

压测周知:每个压测开始执行时,都会向压测周知群里推送消息,包括测试的每秒查询率 (QPS)、测试持续时间等关键信息。

压测事件:在压测开始执行时,Rhino 平台还会向目标服务的事件队列发布一个测试事件,以便快速判断和定位稳定性问题是否由压测引起,从而降低研发人员在线上环境问题排查时的干扰。
压测开关管理
在进行压测之前,必须确保整体链路的压测开关的开启,否则压测流量就会被服务拒绝,导致压测失败。
一键开启:在压测执行之前,Rhino 平台可以一键开启链接上所有节点的压测开关。

压测开关开启周知:压测开关开启时,Rhino 平台会自动给对应服务 Owner 推送相关信息,确保服务 Owner 了解相关压测信息,上游会有压测流量会经过其服务。

静默关闭:压测开关到期后,Rhino 会自动静默关闭压测开关,以保证线上服务的安全。

服务 Mock
对于调用链中不能压测的服务 (敏感服务),或者第三方服务,为了压测请求的完整性,就需要对这些服务进行 Mock。业界通用的 Mock 方案有:
直接更改业务代码,将服务调用替换为空运行代码。这种方法的优点是成本较低,但缺点是返回值过于固定,且对代码和业务的侵入性较强,特别是在 Mock 位置较为下游时,如果超出了部门所覆盖的业务范围,推动实施将变得异常困难。

采用通用的 Mock 服务。通用 MockServer 能够根据用户的不同配置,执行相应的响应延迟,并返回相应的响应数据。这种方法的优点是对业务无侵入性,但缺点是实现成本相对较高。

由于字节跳动全面采用了微服务架构,一次压测往往需要涉及较长的链路,因此快速且无需业务侵入的 Mock 方式成为了优先选择。Rhino 平台通过公司的 Service Mesh 和 ByteMock 系统实现了高效且对业务透明的服务模拟。
在压测执行前,Rhino 平台需要向 Service Mesh 注册染色转发规则,并向 Mock 服务注册 Mock 规则。然后,在压测流量中注入 Mock 染色标记,从而完成服务 Mock:
基于 Service Mesh 的染色流量转发。首先,在压测流量中注入转发染色标记,并在 Service Mesh 中注册对应的转发规则。当 Service Mesh 检测到染色流量时,它会将其转发到指定的 Mock Server 上,如图。

基于 Mock Server 的请求规则匹配。首先在 Mock Server 上注册 Mock 规则,以及匹配的 Response 和响应时延。当 Mock Server 接收到请求后,会根据规则进行响应,如图。

3.4 发压模式

最小调度单元
Rhino 平台中,压测 Agent 就是一个最小调度单元。一次压测任务,通常会拆分成多个子 Job,然后下发到多个 Agent 上来完成。
最小化容器部署,减少资源浪费。压测对机器资源消耗是非常高的,通常 CPU &Memory 的使用率都在 80% 以上。但是没有压测执行时间内,机器资源使用率<5%。如果长期占用大量的资源,将会对机器资源造成极大的浪费。压测 Agent 都采用容器化部署,并且每个容器的资源规格也尽可能小,这样既能满足日常压测需求,也不会占用太多的机器资源。

独占 Agent,增加压测执行稳定性:单个容器内只启动一个 Agent 进程,单个 Agent 同时只能被一个压测任务占用,避免多任务多进程的干扰和资源竞争,增加压测的稳定性。

动态扩容,支撑海量 QPS 发压:压测高峰期,Rhino 平台会临时申请机器资源,快速扩容,完成海量 QPS 的支撑。压测完成后,会立即释放机器资源,减少资源浪费。

2020 年春节抢红包压测中,Rhino 临时扩容在 4000+ 个实例,成功支撑了单次 3kw+QPS 的压测,但日常 Rhino 平台只部署了 100+ 个实例,就能满足日常压测需求。
智能压力调节
动态分配压测 Agent:在压测过程,经常出现压测 Agent 的 CPU/Memory 使用率过高 (>90%),导致压力上不去,达不到目标 QPS;或者压测延时过高,压测结果不准确的问题。Rhino 平台在发压的过程中,会实时监控每个压测 Agent 的 CPU/Memory 使用率,当超过阈值时 (>90%),会动态分配额外的 Agent,以降低每个 Agent 的负载,保证压测的稳定性。

智能调节压力:在压测过程,通常需要不断的调节 QPS 大小,以达到性能压测目标。这过程非常耗费精力和时间。Rhino 平台,可以根据压测任务设定的性能指标,智能调节 QPS 大小,一旦达到压测目标后,会自动熔断,停止压测。

压测链路模拟
Rhino 平台默认将全链路压测分为公网压测和内网压测。公网压测主要 IDC 网络带宽,延时,IDC 网关新建连接、转发等能力;内网压测,主要是对目标服务和目标集群的性能和容量等进行测试。
对于内网压测,默认都要求同 IDC 内发压,减少网络延时的干扰。

对于公网压测,Rhino 平台在公司 CDN 节点上都有部署 Agent 节点,利用了 CDN 节点剩余计算能力,完成了公网压测能力的建设。

同城多机房,异地多机房
Rhino 平台在各个 IDC 都有部署 Agent 集群。各个 IDC 内服务的压测,默认会就近选择压测 Agent,以减少网络延迟对测试结果的影响,从而使测试结果更加准确,更容易定位问题。
边缘计算节点 Agent
除了多机房部署之外,Rhino 平台还在边缘计算节点上也部署了压测 Agent,以模拟不同地区和不同运营商的流量请求,确保流量来源和分布更接近实际情况。在 Rhino 平台上可以选择不同地域不同运营商,从全国各个地区发起压测流量。

3.5 压测熔断

为了应对线上压测风险,Rhino 平台提供两种熔断方式,来应对压测过程中的突发事件,来降低对线上服务造成的影响。
基于告警监控的熔断
每个压测任务,都可以关联调用链中任意服务的告警规则。在压测任务执行过程,Rhino 平台会主动监听告警服务。当调用链中有服务出现了告警,会立即停止压测。对于没有关联的告警,Rhino 平台也会记录下来,便于压测问题定位。

基于 Metric 的熔断
自定义监控指标及阈值,到达阈值后,也会自动停止压测。目前支持 CPU、Memory、 上游稳定性、错误日志,以及其他自定义指标。
此外,除了 Rhino 平台自身提供的熔断机制以外,公司服务治理架构也提供了很多额外的熔断机制,如压测开关,一键切断压测流量;过载保护,服务过载时自动丢弃压测流量。

3.6 任务模型

HTTP 任务
对于 HTTP 协议,参考了 Postman,全部可视化操作,保证所有人都能上手操作,极大降低了压测的使用门槛和成本。

RPC 任务
对于 RPC 任务,Rhino 也自动完成了对 IDL 的解析,然后转换成 JSON 格式,便于用户参数化处理。

自定义-Go Plugin
对于非 HTTP/RPC 的协议,以及有复杂逻辑的压测任务,Rhino 平台也提供了完善的解决方案——Go Plugin。
Go Plugin 提供了一种方式,通过在主程序和共享库直接定义一系列的约定或者接口,就可以动态加载其他人编译的 Go 语言共享对象,使得主程序可以在编译后动态加载共享库,实现热插拔的插件系统。此外主程序和共享库的开发者不需要共享代码,只要双方的约定不变,修改共享库后也不再需要重新编译主程序。

用户只要根据规范要求,实现一段发压业务逻辑代码即可。Rhino 平台可以自动拉取代码,触发编译。并将编译后的插件 SO 文件分发到多个压测 Agent。Agent 动态加载 SO 文件,并发运行起来,就可以达到压测的目的。此外,Rhino 还针对常见 Go Plugin 压测场景,建立了压测代码示例代码库。对于压测新手,简单修改下业务逻辑代码,就可以完成压测了。这样就解决了非常见协议,以及复杂压测场景等的压测问题。

3.7 压测引擎

单 Agent 多引擎
压测调度的最小单元是压测 Agent,但是实际每个 Agent 中有挂载多种压测引擎的,来支撑不同的压测场景。Rhino 平台在压测数据和压测引擎之间增加了一个压测引擎适配层,实现了压测数据与压测引擎的解耦。压测引擎适配层,会根据选择不同的压测引擎,生成不同 Schema 的压测数据,启用不同的引擎来完成压测,而这些对用户是透明的。

压测引擎
在压测引擎方面,我们有开源的压测引擎,也有自研的压测引擎。
开源测试引擎因其广泛的维护人员、全面的特性、稳定的性能和优秀的可靠性而广受欢迎,但其输入格式的限制和高度定制化的难度较大。此外,由于 Agent 通常与开源测试引擎在不同的进程中运行,进程间的通信问题可能会带来控制上的挑战。
相对而言,自研测试引擎虽然在性能上可能略逊一筹,但它通常与 Agent 在同一进程内运行,使得控制变得更加便捷。而且,得益于 Golang 原生支持高并发处理,自研与开源引擎在性能上的差距并不大。
HTTP 协议:我们默认使用 Gatling 作为测试工具,它在单机压测方面的性能非常卓越,远超 Jmeter。在面对智能压测或动态调节的需求时,我们会切换到自研测试引擎。

RPC 协议:我们主要采用自研引擎,利用 Golang 的协程和 RPC 连接池来实现高并发压测。

GoPlugin 协议:基于自研引擎,借助 Golang 插件动态加载的特性,自动加载自定义的压测插件以完成测试。

3.8 压测监控

客户端监控
在监控方面,由于公司监控系统的时间粒度最小为 30 秒,将 30 秒内的数据聚合成一个监控点,这对于压测来说可能不够精细。因此,Rhino 平台自主开发了一套客户端监控系统。
每个 Request 都会以请求开始时间为基准打一个点。

单个 Agent 内,会将相同任务相同接口,1s 内的打点数据在本地做一次汇总,上报到 Kafka 中。

监控服务会消费 Kafka 中的打点数据,将多个 Agent 上报的数据进行再次汇总,然后写入数据库中。

前端监控报表会实时拉取数据库中监控汇总数据,绘制实时监控曲线。

在监控数据汇总流程中,对于请求响应时间的 PCT99 计算,是比较难处理的:

目前 Rhino 平台采用的 T-Digest 算法来计算 1 秒内的 PCT99。

整个时间段内的 PCT99 的计算,则是以 PCT & AGV 的方式聚合。即单位时间内通过 T-Digest 计算 PCT99;整个时间段内的 PCT99,则是对所有点的 PCT99 取平均值。

整体计算方案已与公司服务端监控算法对齐,目的是减少客户端监控与服务端监控之间的 Gap,减少压测结果分析的干扰因素。

服务端监控
服务端监控,直接接入了公司 Metric 系统。
在压测过程中,Rhino 平台会提供整条链路上所有节点核心指标的监控大盘,并高亮显示可能存在风险的节点,来提供实时预警。

对于每个节点也都提供了实时的,详细的监控曲线图。

对于每个节点默认提供 CPU、Memory、QPS 和 Error_Rate 等核心监控指标,用户可以在 Rhino 平台上修改监控配置,增加其他自定义监控指标。

性能 Profile
在压测过程中,Rhino 平台还可以实时采集目标服务进程的性能 Profile,并通过火焰图的方式展示出来,方便用户进行性能问题分析和优化,如图。

4、压测实践

Rhino 压测平台是一个为字节跳动全公司研发人员提供的一站式解决方案,它的核心目标是为了满足公司内所有重点项目和业务的高性能需求。Rhino 的开发团队不仅负责平台的搭建和日常维护工作,还与 QA&RD 团队紧密合作,以确保公司对性能的严格要求得到满足。

4.1 重大项目支撑

在公司的关键项目中,Rhino 平台始终积极参与,并提供全面的支持。例如,抖音春晚活动、西瓜百万英雄挑战以及春节期间的红包雨活动等。这些活动都对平台的性能提出了极高的要求,而 Rhino 团队成功地应对了这些挑战。
以字节跳动春节期间的红包雨活动为例,Rhino 团队承担了整个活动的压测工作。这是一个在春节期间,字节跳动所有客户端共同参与的超大规模红包引流活动,包括抽奖分现金、红包锦鲤、红包雨等。这样的活动带来了巨大的流量,流量突发性强,业务逻辑和网络架构复杂,对 Rhino 平台提出了巨大的挑战。
在红包雨活动中,所有用户流量首先通过运营商专线接入到网络边缘的汇聚机房,经过过滤和验证后,再转发到核心机房。各个 IDC 之间相互备份,具体的流量路线如图所示。在这里,我们需要验证后端服务是否能承受预期的流程,以及各个专线带宽、网关带宽和转发能力、IDC 的承载能力以及它们之间的带宽等。
为了简化压测复杂性,也降低压测问题定位的难度,我们将整个压测拆分成多个阶段:
通过拨测/CDN 压测来分别验证各个汇聚机房的承载能力、带宽以及网关性能。

在各个汇聚机房部署压测 Agent,模拟用户流量分布,以测试核心机房后端服务的性能。

进行单一接口单一实例的压测,单一接口单一机房的压测,场景化的全链路单机房压测,以及场景化的全链路全资源压测,分阶段验证后端服务的性能。

最后,通过全网络的拨测来模拟真实春节期间红包雨的高峰流量,全面验证系统的性能。

在这些大型项目的支撑中,Rhino 团队不仅积累了丰富的行业知识和架构设计经验,而且深入掌握了开发团队对压测的观点以及他们如何使用平台。这使我们能够发现并解决平台存在的问题,从而推动平台的持续改进和优化。

4.2 日常压测任务支撑

为了支持日常的压测工作,Rhino 平台扮演着极其关键的角色。对于日常压测中遇到的各种问题,我们采用了多种解决方案:
专人 Oncall 值周,一对一指导。

建立了一个详尽完善的压测知识库,不仅介绍了如何使用平台,还包括如何改进压测,如何制定压测方案以及如何定位压测问题。

建立了完善的性能培训体系:定期举办与性能测试相关的分享会,并对 QA 和 RD 团队进行专业的压测培训。

4.3 线上流量调度

Rhino 平台还实现了线上流量的定期调度,以实现线上实例的自动压测 [8]:
逐步将线上流量调度到目标实例,测试服务实例的性能极限,并提供实例性能概况,找出性能瓶颈。

通过长期的流量调度,观察服务实例的性能变化,以监控服务性能的变化趋势。

根据不同资源水位下的实例性能,预估整个集群的容量。完成服务容量预估以及线上风险评估。

采用泳道化的流量调度,可以精确预估服务集群的容量。

其具体实现方案如下:
调整负载均衡中目标实例的权重(Weight)值,逐步增大该权重值,将更多流量集中导向目标实例,直到达到设定的停止阈值。
目前已经有 500+ 微服务接入,每天定时执行流量调度,来监控线上服务性能变化趋势,如下图。

4.4 常态化压测

Rhino 平台正致力于在公司内部推广持续性的压测文化,以提升我们的服务质量和性能。通过定期的自动化全链路压测,我们旨在实现以下目标:
实时监控线上服务集群容量,防止服务性能劣化。

实时监控线上链路容量,防止链路性能劣化。

为了实现这一目标,Rhino 平台上的持续性压测会按照预定的周期定时执行,以无人值守的方式自动完成压测任务,并将测试结果实时推送。在测试执行过程中,会根据调用链自动开启压测开关,发起压测流量。同时,实时监控服务性能指标,并根据性能指标和告警监控,自动完成压测熔断,以确保测试的安全性。
目前,多个业务部门已经接入持续性压测,以此确保线上服务的稳定性。此外,我们还提供了一系列的分析和报告工具,帮助开发人员和运维团队理解和优化测试结果。
Rhino 平台通过持续性压测和提供一系列的分析报告工具,确保了线上服务的稳定性和性能。通过定期的自动化全链路压测,我们实现了实时监测线上服务集群和链路的容量,防止服务性能降低。此外,持续性压测还确保了测试的安全性,避免了潜在的风险。通过这些措施,我们进一步提升了公司的服务质量和性能,确保了业务的持续发展。

4.5 DevOps 流水线中的压测

在服务上线的过程中,通常会经历预发布、线上小流量灰度、线上全量发布等阶段。在这些阶段,我们可以通过线上测试用例和灰度发布来发现和拦截线上功能缺陷。然而,对于性能缺陷的发现和拦截,这种方法并不足够有效。
线上故障追踪系统揭示了一个重要问题:由于上线前缺乏性能压测,许多性能缺陷未能被及时发现,导致线上问题。
为了更有效地发现和识别性能缺陷,Rhino 平台已经与 DevOps 平台实现了深度集成。我们将压测服务作为 DevOps 平台中的一个独立服务进行注册,并允许开发人员将压测环节插入到任意流水线的任意位置,确保在服务上线前进行必要的性能压测。DevOps 流水线中的集成压测不仅帮助开发人员发现代码中的性能问题,还能通过与性能基线的对比,揭示代码性能退化的趋势。
为了进一步提升性能测试的效率和准确性,我们还提供了一系列的性能测试工具和指标,帮助开发人员和运维团队更好地理解和分析测试结果,从而在上线前及时发现并解决性能问题。
Rhino 压测平台在公司的关键项目和业务中发挥着重要作用,确保服务质量和性能得到满足。通过定期的自动化全链路压测,我们实现了实时监控线上服务集群和链路的容量,防止服务性能下降。此外,Rhino 平台已经与 DevOps 平台实现了深度集成,确保在服务上线前进行必要的性能压测。通过集成压测,开发人员可以及时发现代码中的性能问题,并对比性能基线以识别性能退化趋势。此外,我们还提供了一系列的性能测试工具和指标,以帮助开发人员和运维团队更好地理解和分析测试结果。总之,Rhino 平台通过持续性压测和与 DevOps 平台的集成,确保服务在上线前具备高性能,满足公司的业务需求。

5、总结与展望

5.1 总结

Rhino 压测平台从立项到现在,不到两年的时间内,其发展已经初具规模,如图(每月压测执行统计)。这个期间,非常非常感谢公司内所有合作团队,尤其是架构团队,中台团队对压测平台的支撑,没有他们的支撑,全链路压测建设是难以完成的。

5.2 未来发展

  • 业务深层次定制化 通用压测平台已经初步搭建完成,能够满足大多数业务线的日常测试需求,但在实际支持过程中,我们发现不同业务线在进行压测时,仍需完成许多前期和后期工作,这些往往需要人工介入。 为了进一步减少业务方在压测改造上的成本,降低环境数据预置的费用,加快测试数据清理的速度,以及更快速地定位性能问题,Rhino 压测平台计划更深入地融入业务场景,与各业务部门深化合作,提供更精细化的业务定制服务,以提高研发效率,推动业务线的持续发展。
  • 压测与容量规划 在业务资源管理方面,我们需要回答的问题包括:当前的资源是否足够,业务的容量有多大;在当前的业务增长速度下,现有的机器资源能够维持多久? 同时,我们也要审视现有服务资源的利用情况,探讨如何优化以提高资源利用率,并降低机器成本。在面对大型活动时,如何合理申请和分配资源,是否可以避免不必要的压测,或者通过自动化手段利用线上流量数据,或借鉴日常测试数据,来得出合理的结论,这些都是我们未来需要关注的焦点。
  • 压测与 SRE 在确保服务稳定性的同时,如何有效监控服务性能的下降并及时发出预警,如何合理配置限流、超时、重试和熔断等治理措施,以及如何与混沌测试结合进行容灾演练,保证服务的稳定性,这些都是 Rhino 平台未来将要深入探索的领域。我们致力于通过这些努力,不断提升服务的可靠性和性能,以满足业务发展的需求。 更多内容可以学习《测试工程师 Python 工具开发实战》书籍《大话性能测试 JMeter 实战》书籍
共收到 1 条回复 时间 点赞
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册