做了一次线上压测,整个团队都......
通过监控的报表和一些报警规则的设置,你能实时跟踪和解决垂直电商系统中出现的问题。但监控只能发现目前系统中已存问题,对未来可能发生性能问题无能为力。
一旦你的系统流量有大长,比如大促活动流量,那你在面临性能问题时就可能手足无措。你需要了解在流量增长若干倍时,系统的哪些组件或者服务会成为整体系统的瓶颈点,这时你就需要做一次全链路压测。
1 压力测试是啥?
1.1 错误的压测姿势
- 搭建一套与生产环境功能相同的测试环境,并导入或生成一批测试数据
- 在另一台服务器,启动多线程并发调用待压测接口(接口参数也设置相同,如想压测获取商品信息的接口,则压测时使用同一商品ID)
- 最后,通过统计访问日志或查看测试环境的监控系统,记录最终压测QPS
错误点
做压测时,最好使用生产数据、生产环境
因你无法确定自己搭的测试环境和正式环境差异点,是否会影响压测结果
压测时不能使用模拟请求,应该使用生产流量。可通过流量复制,把线上流量复制一份到压测环境。因为模拟流量的访问模型和线上流量相差很大,会对压力测试的结果产生大影响。
比如,你在获取商品信息时,线上流量会获取不同商品的数据,有的命中缓存,有的没命中。若使用同一商品ID压测,则只有第一次请求没有命中缓存,而在请求之后会将数据库中的数据回种到缓存,后续请求就一定会命中缓存,这种压测数据不具备参考性。
不要从一台服务器发起流量,这样很容易达到这台服务器性能瓶颈,从而导致压测QPS上不去,最终影响压测结果。而且,为尽量真实模拟用户请求,倾向于把流量产生的机器放在离用户更近位置,如CDN节点。如果没有这个条件,那么可以放在不同的机房中,这样可以尽量保证压力测试结果真实性。
所以,并非只要是使用多个线程并发的请求服务接口,就算是对接口进行压力测试了。
什么是压测
压测指在高并发大流量下进行的测试,测试可通过观察系统在峰值负载下的表现,找到系统性能隐患。
类似监控,压测是一种常见的发现系统问题的方式,也是保障系统可用性和稳定性的重要手段。
压测过程,不能只针对某一核心模块做压测,而需要将接入层、所有后端服务、数据库、缓存、消息队列、中间件及依赖的第三方服务系统及其资源,都纳入压力测试目标。因为,一旦用户访问行为增加,包含上述组件服务的全链路都会受到流量冲击,都需要依赖压测发现性能瓶颈,这种针对整个调用链路执行的压测称为“全链路压测”。
互联网项目功能迭代快,系统复杂性也越来越高,新增功能和代码很可能会成为新性能瓶颈。所以,压测应作为系统稳定性保障的常规手段,周期进行。
但通常做一次全链路压力测试,需联合DBA、运维、依赖服务方、中间件架构等多个团队,一起协调进行,人力、沟通协调成本高。压测过程中,若无监控机制,还会对线上系统造成不利影响。
怎么解决这些问题呢?
搭建一套自动化的全链路压测平台来降低成本和风险!
2 全链路压测平台
2.1 搭建的核心
2.1.1 流量隔离
压测是在生产环境,所以需区分压测流量和正式流量,这样可针对压力测试的流量做单独处理。
2.1.2 风险控制
尽量避免压测对正常访问用户的影响。
因此,全链路压测平台需包含:
- 流量构造和产生模块
- 压测数据隔离模块
- 系统健康度检查和压测流量干预模块
2.2 全链路压测系统架构
2.2.1 压测数据的由来
系统入口流量一般来自客户端HTTP请求。故可在系统高峰期,将这些入口流量复制一份,经过流量清洗后(比如过滤一些无效请求),将数据存储在HBase、MongoDB这些NoSQL或亚马逊S3这些云存储服务,称之为流量数据工厂。等压测时,就能从工厂直接获取数据,将数据切分多份后下发到多个压测节点。
如何实现流量复制?
最简单粗暴的:直接复制负载均衡服务器的访问日志,数据就以文本方式写入流量数据工厂。但这样的数据在发起压测时,需要自己写解析脚本来解析访问日志,增加压测成本,不推荐。
开源工具。推荐GoReplay,可劫持本机某端口的流量,将它们记录在文件,传送到流量数据工厂。压测时,使用该工具进行加速的流量回放,就实现了对正式环境的压测。
下发压测流量时,需保证下发流量的节点与用户更加接近
起码不能和服务部署节点在同一个机房中,可尽量保证压测数据的真实性。
对压测流量染色
即增加压测标记。在实际项目中,我会在HTTP的请求头中增加一个标记项,比如说叫做is stress test,在流量复制后,批量在请求中增加这个标记项,再写入数据流量工厂。
2.2.2 数据如何隔离
将压测流量拷贝下来的同时,也需考虑对系统做改造,以实现压测流量和正式流量的隔离,这样一来就会尽量避免压测对线上系统的影响。一般来说,我们需要做两方面的事情。
一方面,针对读取数据的请求(一般称之为下行流量),我们会针对某些不能压测的服务或者组件,做Mock或者特殊的处理,举个例子。
在业务开发中,我们一般会依据请求记录用户的行为,比如,用户请求了某个商品的页面,我们会记录这个商品多了一次浏览的行为,这些行为数据会写入一份单独的大数据日志中,再传输给数据分析部门,形成业务报表给到产品或者老板做业务的分析决策。
在压测的时候,肯定会增加这些行为数据,比如原本一天商品页面的浏览行为是一亿次,而压测之后变成了十亿次,这样就会对业务报表产生影响,影响后续的产品方向的决策。因此,我们对于这些压测产生的用户行为做特殊处理,不再记录到大数据日志中。
再比如,我们系统会依赖一些推荐服务,推荐一些你可能感兴趣的商品,但是这些数据的展示有一个特点就是展示过的商品就不再会被推荐出来。如果你的压测流量经过这些推荐服务,大量的商品就会被压测流量请求到,线上的用户就不会再看到这些商品了,也就会影响推荐的效果。
所以,我们需要Mock这些推荐服务,让不带有压测标记的请求经过推荐服务,而让带有压测标记的请求经过Mock服务。搭建Mock服务,你需要注意一点:这些Mock服务最好部署在真实服务所在的机房,这样可以尽量模拟真实的服务部署结构,提高压测结果的真实性。
另一方面,针对写入数据的请求(一般称之为上行流量),我们会把压测流量产生的数据写入到影子库,也就是和线上数据存储完全隔离的一份存储系统中。针对不同的存储类型,我们会使用不同的影子库的搭建方式。
- 如果数据存储在MySQL中,我们可以在同一个MySQL实例,不同的Schema中创建一套和线上相同的库表结构,并且把线上的数据也导入进来。
- 而如果数据是放在Redis中,我们对压测流量产生的数据,增加一个统一的前缀,存储在同一份存储中。
- 还有一些数据会存储在ES,针对这部分数据,我们可以放在另外一个单独的索引表中。
通过对下行流量的特殊处理以及对上行流量增加影子库的方式,我们就可以实现压测流量的隔离了。
压力测试如何实施
在拷贝了线上流量和完成了对线上系统的改造之后,我们就可以进行压力测试的实施了。在此之前,一般会设立一个压力测试的目标,比如说,整体系统的QPS需要达到每秒20万。
不过,在压测时,不会一下子把请求量增加到每秒20万次,而是按照一定的步长(比如每次压测增加一万QPS),逐渐地增加流量。在增加一次流量之后,让系统稳定运行一段时间,观察系统在性能上的表现。如果发现依赖的服务或者组件出现了瓶颈,可以先减少压测流量,比如,回退到上一次压测的QPS,保证服务的稳定,再针对此服务或者组件进行扩容,然后再继续增加流量压测。
为了能够减少压力测试过程中人力投入成本,可以开发一个流量监控的组件,在这个组件中,预先设定一些性能阈值。比如,容器的CPU使用率的阈值可以设定为60%~70%;系统的平均响应时间的上限可以设定为1秒;系统慢请求的比例设置为1%等等。
当系统性能达到这个阈值之后,流量监控组件可以及时发现,并且通知压测流量下发组件减少压测流量,并且发送报警给到开发和运维的同学,开发和运维同学就迅速地排查性能瓶颈,在解决问题或者扩容之后再继续执行压测。
业界关于全链路压测平台的探索有很多,一些大厂比如阿里、京东、美团和微博都有了适合自身业务的全链路压测平台。在我看来,这些压测平台万变不离其宗,都无非是经过流量拷贝、流量染色隔离、打压、监控熔断等步骤,与本课程中介绍的核心思想都是相通的。因此,你在考虑自研适合自己项目的全链路压测平台时,也可以遵循这个成熟的套路。
总结
- 压测是一种发现系统性能隐患的重要手段,所以应该尽量使用正式的环境和数据
- 对压测的流量需要增加标记,这样就可以通过Mock第三方依赖服务和影子库的方式来实现压测数据和正式数据的隔离
- 压测时,应该实时地对系统性能指标做监控和告警,及时地对出现瓶颈的资源或者服务扩容,避免对正式环境产生影响。
这套全链路的压力测试系统对于我们来说有三方面的价值:其一,它可以帮助我们发现系统中可能出现的性能瓶颈,方便我们提前准备预案来应对;其次,它也可以为我们做容量评估,提供数据上的支撑;最后,我们也可以在压测的时候做预案演练,因为压测一般会安排在流量的低峰期进行,这样我们可以降级一些服务来验证预案效果,并且可以尽量减少对线上用户的影响。所以,随着你的系统流量的快速增长,你也需要及时考虑搭建这么一套全链路压测平台,来保证你的系统的稳定性。
#java实习#