全链路压测的大概思路
参与了我们业务的全链路压测,虽然过程磕磕绊绊,压测的当天晚上还在写压测脚本,但是核心链路的压测还是做了起来,效果也不错,当前晚上就爆出了一个P1级的bug。这篇文章就总结下如何做核心链路的全链路压测。
时机
首先要清楚的一点就是,什么时候开始做全链路压测?我们有另外一个业务线,现在就没有打算做,那个业务线的日均单不到十万,而要压测的业务线的日均单到了200万,但这并不意味着200万是一个标准,我觉得可以从下面几点考虑:
- 业务发展速度。在可以预期的一段时间(最好是半年,一个季度有点晚)内,业务会有较快速的发展,线上机器必须要大幅度扩容;但是,扩容有的时候并不是线性的,从两台扩展到四台,你得服务能力或者能提高两倍,但是在继续扩容,服务能力就有可能提高不上去了,因为要受限于其他的模块,比如,DB,公共组建,中间件等等
- 链路的复杂程度在扩张。一般而言,随着业务的发展,我们的接口会越来越多,系统会逐渐的做分布式,业务线内部的模块越抽象越多,业务线跟其他业务线的交互也也越来越多,我们无法单纯的根据自己系统的处理能力来评估接口的服务能力。
- 对单机压测结果越来越没有自信。这也是一个很好的指标,一般而言,我们都会压一下我们自己的模块,但是身为模块的owner,自己越来越清楚,单机的压测不代表真实的场景,内心会越来越虚,这个时候,就要考虑全链路了。
方法
下面具体看看要做全链路需要哪些工作。
梳理核心链路的流程和边界
因为全链路一定会设计多个流程,多种技术,多个依赖,所以,要做全链路压测,首先要梳理核心链路的流程,明确链路的边界,我觉得梳理这个是比较简单的,因为一个业务再复杂,它的核心链路肯定有限,例如,我们的核心链路就包括:
- 创建订单
- 开始形成
- 获取行程费用
- 结束订单
- 支付订单
- 完单
核心链路是一个业务的核心,这一块应该可以很快梳理清楚,但是,难点在于梳理清楚链路的边界。例如:
- 开始订单要做风控
- 结束订单要发券
- 结束订单要通知用户费用
- 完单后要通知营销
- 。。。
在核心链路的基础上,我们会有很多的分支业务,而这些分支业务有的可以参与压测,有的不能参与压测:原因多种多样,比如,这个分支业务不是我们自己公司的,或者这个分支业务本身就不怎么重要,可以降级掉,甚至有的业务就是不能压测,比如给用户下放push消息。
在具体实施的时候,业务反复跟整个链路的每个业务owner反复确认,哪些是核心业务,哪些是分支业务,哪些参与压测,哪些不参与压测,把这些形成文档,逐个跟进。
提供全链路压测的底层支持
要做全链路,要实现非核心链路的降级,就必须对底层的产品,例如中间件,数据库访问,MQ等做改动,让这些中间件支持全链路压测。我们整体看看,一般需要哪些改动。
我们把模型简化一下,如下图,虽然是简化的,但是基本上包括主流的分布式业务的技术栈。
可以看到,底层主要需要提供下面的支持:
- 全链路透传压测标志:必须有一种在全链路透传压测标志的能力,并且必须基于一次请求,也就是同一个traceId,现在,大部分分布式业务都会接入trace系统,例如,google的dapper,阿里的鹰眼等,对trace系统进行改造,使其能够透传压测标志,需要透传的路径大概有:
- HTTP,RPC(DUBBO),MQ,线程池等
- 影子表:参与压测的业务,要逐个排查自己依赖的数据库,然后创建影子表,影子表必须跟正常表的schema保持一致,可以在每次压测时候手动创建,也可以推动DBA自动创建。创建好影子表后,如果当前流量是压测流量,那么写入和读取都走影子表。如果有自己的数据库中间件最好,没有的话可以借助于Mybatis的Interceptor机制。
- 日志-影子目录:为了防止压测流程的日志对正常日志造成干扰,需要改造日志组件,将压测流量产生的日志落入到影子目录。影子目录可以有日志组件自动创建。
- MQ支持是否消费压测流量:有的时候,全链路会通过MQ进行传递,所以,必须在消费MQ的时候进行选择:是否选择消费压测流量的MQ消息。这就需要对MQ系统进行改造,一方面使其可以透传压测流量,另一方面,需要支持配置是否消费压测的MQ消息
- 缓存,大数据隔离:还有一些场景,比如,缓存层,大数据层对压测流量的处理也要考虑隔离。缓存可以使用不同的集群;大数据可以直接不收集压测的数据。
思考全链路压测的数据怎么mock
流程支持之后,还有关键的一步,就是考虑如何构造压测的mock数据。在使用影子表之后,可以比较轻松的实现跟正常数据隔离,那剩下的就是好构造好mock数据,有几点需要考虑:
- 用户数据要提前做好认证等准备工作
- Mock数据要尽可能跟真实数据保持一致,比如,价格水平,图片数量,地址信息等等
- Mock数据有些限制需要放开,比如,库存,一些运营性质的活动可以取消等
- 千万不要污染正常数据:认真梳理数据处理的每一个环节,确保mock数据的处理结果不会写入到正常库里面
做好压测流量的降级预案
这一点尤其重要,特别当业务特别的复杂的时候,一定要确认好,第三方依赖能不能接收压测流量,所以,只要依赖第三方的服务,我们都要接入压测流量降级的开关,防止对第三方服务的污染。实现上,可以集成到RPC机制上,也可以提供类似于单独的限流组件。
梳理监控体系
确认好流程的技术支持和Mock数据的支持后,还要让每个业务梳理自己的监控,确保压测时候能够准确,及时的发现问题。
- 核心接口和核心依赖的流量和耗时监控
- 中间件组件,缓存,数据库的监控报警
- 机器的指标报警
线下做好预演
真实的压测之前,肯定要进行预演,预演主要确认:
- 压测流程是否写入到了正确的目的地,例如,写入到影子表,影子目录,压测cache等等
- 压测流量的降级是否完备和有效
- 进一步确保监控都已到位
尽量模拟现实
我们压测的脚步要尽可能的模拟现实,比如:
- 购买的行为:不是下单后立即购买,而是要等一下子
- 骑车子的行为:开锁后并不是里面换车,而是骑一会
- 用户付款的行为:压测时候肯定不会真的让用户付款,所以我们得模拟用户付款
- 购买商品的数据
- 。。。
压测的脚步要跟各个业务确认,尽量跟线上真实的用户行为保持一致
逐步平滑加压
压测的时候,逐步加压,并且要保持平滑加压,不要把一秒的流量都在前面几毫秒内都压出去。
形成报告
每次压测后进行复盘,总结压测中的问题,压测结果,机器的指标等数据形成报告发给大家,制订好后续的Action以及跟进的负责人。
推进
全链路压测的技术难点不多,除了要花时间梳理流程和思考如何处理数据之外,最难的就是整个链路跨多个业务,甚至部门,需要跟进每个业务线的进度,确保大家能够在给定的时间点进行联调以及进行压测。在推进的时候,按照核心链路所在的模块进行跟进,每个模块出一个owner,各个owner跟进核心的接口和依赖,每周大家碰一下同步下总体的进度。