全链路压测
1. 背景
最早是阿里提出来的,天猫双十一…
QPS等概念
- QPS:Queries Per Second意思是“每秒查询率”,是一台服务器每秒能够相应的查询次数
- TPS:是TransactionsPerSecond的缩写,也就是事务数/秒。它是软件测试结果的测量单位。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
- RT(Response-time):响应时间:执行一个请求从开始到最后收到响应数据所花费的总体时间,即从客户端发起请求到收到服务器响应结果的时间
- 并发数是指系统同时能处理的请求数量,这个也是反应了系统的负载能力。
- 系统的吞吐量(承压能力)与request对CPU的消耗、外部接口、IO等等紧密关联。单个request 对CPU消耗越高,外部系统接口、IO速度越慢,系统吞吐能力越低,反之越高。系统吞吐量几个重要参数:QPS(TPS)、并发数、响应时间。
QPS(TPS)
:(Query Per Second)每秒钟request/事务 数量并发数
:系统同时处理的request/事务数响应时间
:一般取平均响应时间
QPS = 并发数/平均响应时间
- 实际举例
- 如果每天80%的访问集中在20%的时间里,那么这20%的时间就叫做峰值时间
- 公式:峰值时间每秒请求数QPS=总PV数0.8 / 每天秒数0.2
- 每天300w PV 的在单台机器上,这台机器需要多少QPS?
- ( 3000000 * 0.8 ) / (86400 * 0.2 ) = 139 (QPS)
- 如果一台机器的QPS是58,需要几台机器来支持?
- 139 / 58 = 3
最佳线程数
- 单线程QPS公式:QPS=1000ms/RT
- 对同一个系统而言,支持的线程数越多,QPS越高。假设一个RT是80ms,则可以很容易的计算出QPS,QPS = 1000/80 = 12.5
- 多线程场景,如果把服务端的线程数提升到2,那么整个系统的QPS则为 2*(1000/80) = 2
- 实际的QPS、RT关系如下
最佳线程数量,刚好消耗完服务器的瓶颈资源的临界线程数,公式如下
最佳线程数量=((线程等待时间+线程cpu时间)/线程cpu时间)* cpu数量
1.1 什么是全链路压测?
基于实际的生产业务场景和系统环境,模拟海量的用户请求和数据,对整个业务链路进行各种场景的测试验证,持续发现并进行瓶颈调优,保障系统稳定性的一个技术工程。
1.2 全链路压测解决了什么问题?
针对业务场景越发复杂化、海量数据冲击,发现并解决整个业务系统的可用性、扩展性以及容错性的过程。
1.3 全链路压测创造了什么价值?
技术角度:降低成本、提高服务可用性、技术练兵&团队协作&快速响应;
业务角度:提升用户体验、技术更好的服务业务、创造更多业务价值。
- 保证系统稳定性:可能提前预估系统存在的各种问题,提前拟高并发场景,有备无患。
- 请求链路追踪,故障快速定位:可以通过调用链结合业务日志快速定位错误信息。
- 精准的容量评估:能够定位到最需要扩容的服务,帮助公司用最低的成本满足业务的性能要求真实的性能验证:能够在生成环境以最真实的环境来验证系统的真实性能。
- 数据分析,优化链路:可以得到用户的行为路径,汇总分析应用在很多业务场景。
1.4 与传统方式的对比
压测类型 | 传统压测 | 全链路压测 |
---|---|---|
压测方式 | Jmeter、Locust、Loadrunner | 压测集群、流量引擎、录制回放 |
承接方式 | 需求响应式,被动 | 发现系统所有链路存在的瓶颈点,主动 |
投入成本 | 需要搭建单独的压测环境 | 完全线上生产环境进行,无须单独搭建环境 |
压测环境 | 测试环境/性能环境 | 生产环境 |
压测场景 | 单机单接口、单机单链路、单机混合链路 | 包含覆盖范围内的所有核心链路及场景 |
压测过程 | 可观测性较低,延时较高 | 实时可视化观测 |
1.5 如何展开全链路压测
业务模型梳理
- 首先应该将核心业务和非核心业务进行拆分,确认流量高峰针对的是哪些业务
- 梳理出对外的接口:使用MOCK(模拟)方式做挡板。千万不要污染正常数据:认真梳理数据处理的每一个环节
数据模型构建
- 数据的真实性和可用性:可以从生产环境完全移植一份当量的数据包,作为压据,通过分析历史数据增长趋势,预估当前可能的数据量
- 数据隔离:千万千万不要污染正常数据:认真梳理数据处理的每一个环节,可落入影子库,mock 对象等手段,来防止数据污染
压测工具选型
使用分布式压测的手段来进行用户请求模拟,目前有很多的开源工具可以提供IMeter、nGrinder、Locust等。
2. 全链路整体架构
2.1 核心技术
- 流量染色
- 染色标志穿透微服务
- tomcat线程池复用问题
- 染色标志穿透线程池
- 接口mock
- 数据库隔离
- 影子表:比如在表中加入一个字段,0代表是压测数据,1代表正常数据
- 影子库:放到不同的库下
- 消息队列隔离
- 当生产消息扔到MQ中,接着让消费者消费,这个没有问题,压测的数据不能够直接扔到MQ中
- redis隔离
- 通过key值来区分,压测流量的key值加统一后缀,通过改造RedisTemplate来实现key路由
- rabbitmg隔离
- 全链路服务监控
- 全链路日志隔离:生产日志和压测日志隔离
- 全链路风险熔断
2.2 涉及的业务问题
- 涉及的系统太多,牵扯的开发人员太多
- 模拟的测试数据和访问流量不真实
- 压测生产数据未隔离,影响生产环境
2.3 框架实现
2.3.1 流量染色方案
流量识别
- 全链路压测发起的都是HTTP请求,只需要请求头上添加统一的压测请求头
- 要想压测的流量和数据不影响线上真实的生产数据,就需要线上的集群能够识别出压测数据,只要能识别压测请求的流量,那么流量触发的读写操作就很好的统一去做隔离了
- 通过在请求协议中添加压测请求的标识,在不同服务的相互调用时,一路透传下去,这样每一个服务都能识别处压测的请求流量,这样做的好处就是与业务完全的解耦,只需要应用框架进行感知,对业务方代码无侵入
那么服务怎么识别这个请求的标识呢
tomcat线程池复用问题
tomcat默认使用线程池来管理线程,一个请求过来,如果线程池里面有空闲的线程,那么会在线程池里面取个线程来处理该请求,一旦该线程当前在处理请求,其他请求就不会被分配到该线程上,直到该请求处理完成。请求处理完成后,会将该线程重新加入线程池,因为是通过线程池复用线程,就会如果线程内部的ThreadLocal没有清除就会出现问题,需要新的请求进来的时候,清除ThreadLocal。
fegin传递染色标识
使用fegin来实现远程调用,跨微服务传染色体标识是通过MVC拦截器获取到请求header的染色体标识,并放进Threadlocal中,然后交给Fegin拦截器在发送请求之前从Threadlocal中获取到染色体标识,并放进Fegin构建请求的header中,实现微服务之间的火炬传递
Hystrix传递染色体标识
Hystrix隔离技术主要有两个
- 信号量:信号量的资源隔离只起到一个开关的作用,比如服务A的信号量大小为10,那么就说它同时只允许有10个tomcat线程来访问服务A,其它请求都会等待,从而达到资源隔离和限流保护的作用
- 线程池
线程池隔离技术,用hystrix自己的线程去执行调用,而信号量隔离技术,是直接让tomcat线程去调用依赖服务,信号量隔离,只是一道管卡,信号量有多少,就允许多少个tomcat线程通过它,然后去执行