云集的全链路压测之路
《云集的全链路压测之路》
其实说句实话,我们的全链路压测之路走的还是比较艰辛的,从最初纠结用什么压测工具开始,到压框架、压单接口、压压测环境,到线上环境一步步摸索,并结合其它友商的经验和方案,直至今天,将近耗时一年多的漫长时间,我们才最终在黑暗中摸索出了一条属于我们鸡场人自己的全链路压测之路,我们就是在不停的试错过程中逐步成长和成熟起来的。
一、究竟什么是全链路压测
流量不大的时候,开发人员、测试人员随便在线下做一把功能测试,只要功能能够正常跑通就行了,但随着用户规模线性上涨,流量越来越大,我们逐渐会意识到,光靠常规的功能测试似乎是远远不够的,流量上来了,必须重视系统性能了,毕竟谁也不希望自己的系统被流量无情击垮,要做到心中有数(清楚系统的容量水位,有指导的进行容量规划)。因此到了这个阶段,大多数企业都会选择在线下对框架、中间件、存储层实施压测,明确其吞吐量,但是这样的压测结果数据,离线上还是有很大差距,毕竟绝大多数企业的压测环境并不会与生产环境1:1(至今我也未曾见过这种土豪企业),所以压测环境的压测结果数据纯粹是仅供参考,并不能够作为线上环境的指导数据,只有直接在线上环境实施压测才是唯一的明道。
在线上环境直接进行压测,说来轻松,但其背后却影藏着极大的风险。大多数情况下,我们的系统都是有用户在进行访问的,尤其是峰值流量时绝不能因为压测流量导致系统故障从而影响用户下单,甚至更不能够污染线上数据。试想一下,用户A的订单信息中,突然多出来10台IphoneX,你是送还是不送?或者余额突然变少了,都是用户绝对不能够容忍的(除非变多了,闷声发大财),并且线上环境往往还会伴随着各种定时任务,统计的时候如果把压测数据也计算进收益中,运营部门的老大估计会请研发同学喝茶。
尽管困难重重,但是要探测系统的真实容量水位,有指导的在大促前进行容量规划,以及给出合理的限流水位,只有此路通罗马。因此系统需要能够智能化到准确无误的区分哪些数据是真实用户流量,哪些是压测流量,然后引流到隔离环境中落盘,后面我会讲到如何区分压测流量。
那么究竟什么是全链路压测呢?单接口压测相信大家都清楚,比如对网站A接口进行压测,假设其QPS是10W/s,但在压测网站A接口的时候,同时也在对B接口进行压测,这时网站A接口的QPS必然不会再是10W/s。这是因为系统中任何一个接口都不会单独存在,都会或多或少受到一些公共资源的制约,当公共资源成为瓶颈时,系统全局都会受到影响。简单来说,所谓全链路压测指的其实就是对系统的所有核心链路都同时实施压测,当系统整站流量都被打上来的时候,才会暴露出系统的性能瓶颈,才能够探测出系统整体的真实容量水位,这就是全链路压测以及实施全链路压测的意义。
二、系统如何区分真实用户流量和压测流量
在说道如何区分压测流量之前,我们首先来谈论一个比较敏感的话题,业务系统是否需要有侵入性的进行改造,明确告诉大家,需要!因为全链路压测任务并不会在业务初期就进行,基本上都是在业务后期才会需要,尤其是当业务越来越复杂的时候,改造的难度就会越大。但是企业的基础架构团队应该意识到,绝大多数的流量区分工作都应该是在中间件、Base组件中完成,当然如果完全没有侵入,这几乎是不现实的。
实施全链路压测的重点是:
1、区分压测流量数据;
2、压测流量数据应该落盘到隔离环境中。
最早我们一直都是在线上环境做读接口的压测,并不敢直接一开始就实施读/写并行压测,因为心里没底!这是实话,但当明确如何区分压测流量和清楚如何隔离压测数据后,我们便可以开始大胆的进行真正意义上的线上全链路压测。
先来说说如何区分压测流量:
1、压测流量会统一在URL上打标;
2、接入层接受到请求后,Filter拦截并识别压测标识,放进ThreadLocal;
3、接入层调用服务时,调用链埋点端从ThreadLocal中获取压测标识,并写入上下文中向下进行传递;
4、落盘或者存储时依靠Base组件从服务上下文中获取压测标识区分数据走向。
有些业务是需要调用第三方接口(最典型的就是调用银行支付接口),这种情况下,业务系统中就需要判断如果是打标流量,那么则直接mock,如图1所示:
图1 压测流量识别
当我们明确如何区分真实用户流量和压测流量后,接下来的问题就是压测数据既然不能够污染线上环境,那么究竟应该落到哪里的?我们选取的做法是从2个维度来看(物理隔离和逻辑隔离并存),毕竟我们还不是土豪,没有办法构建跟生产环境1:1的存储系统。
对于那些真正需要落盘的数据(比如:订单数据),我们会将压测数据写入到影子库中,完全隔离开线上环境,这是最安全的,但是成本比较高,毕竟是不同的实例,为了安全起见还是非常有必要的,这便是物理隔离。而逻辑隔离是指,一些中间数据,比如需要写入MQ、Redis等的压测数据,我们采用的做法是逻辑隔离,比如写入Redis时,压测数据的Key统一加上压测标识;写入MQ时,我们会写入到不同的Topic中(由于业务特点和介于RocketMQ本身的实现机制,我们选择了由NameServer路由到固定的几台压测MQ机器上)。
三、压测流量如何下发及压测数据如何构建
在最开始的时候,研发同学和测试同学使用的压测工具似乎是百花齐放的,比如:Jmeter、Apache AB等常规测试工具,但是这类工具无法瞬间发起超大规模的压测流量(难以做到分布式压测),后来我们也考虑过Ngrinder,但是这货也很难胜任全链路压测,因为Controller过于笨重,能够管理Agent的数量是极其有限的,这一点京东已经验证过了,我们也没有必要再走弯路,所以直接催生出了我们要自建全链路压测系统之路的构想。
鸡场的全链路压测军演系统叫做TItan,其整体架构如下所示:
图2 云集全链路压测军演系统(Titan)整体架构
本篇文章不会重点对Titan进行详细介绍,因为我们预计在明年上半年左右会正式开源,并且可以保证的是,开源分支版本将会与云集内部版本同步,更不会对功能进行阉割。
压测流量的下发是由Titan完成的,那么压测数据是如何构建的呢?最早的时候,我们采用的做法是对一些动态参数脚本手工构建,相信大家都知道,这是非常痛苦的一件事情,尤其是一些只能够使用一次的动态参数,如果压测规模较大,那么这将会让人痛不欲生,并且极其容易出错,所以我们接下来的做法便是构建压测数据工厂,压测数据来源于压测数据工厂构建,然后由Titan执行流量下发即可,一条路打通。
码字不容易,如果你觉得文章对你有帮助,请点赞并注明出处转载,多谢!
推荐阅读