把生命浪费在美好事物上

产品产品产品

358篇博客

web性能测试你问我答之测试准备

web性能测试你问我答之测试准备

关于测试环境

Q:环境准备之前需要了解什么

A: 需要了解完整的系统架构,例如了解前端代理服务器是什么,应用服务器是什么,后端依赖有哪些,最好可以整理出一份线上的部署架构,或者即将上线的产品部署架构,画出完整的拓扑图,并且询问清楚线上服务器的配置以及机器个数等信息。

Q:性能测试需要什么样的环境

A: 我们希望性能测试环境的配置最好和线上一致,或者线上配置的子集,如果是云主机环境的话,希望单台服务器的配置和线上一致,即如果线上是4 * 4ecu的机器,性能测试的应用服务器也应该是4 * 4ecu的。对于后端依赖服务器,比如数据库DDB、搜索服务NCS等,这些配置需要DBA确认是否可以压测,能否支撑一定的并发压力,要保证性能瓶颈点不出现在依赖服务上。

Q:云主机环境申请的时候需要注意哪些地方

A:

  1. 单台云主机的配置最好和线上的云主机环境一致:比如线上是 4 * 4 ecu的,性能测试环境应该也是 4 * 4 ecu的,因为不同的ecu数目处理能力差异较大,无法真实评估线上一台云主机的性能到底是怎样的。
  2. 一般情况下不需要配置云硬盘,但是如果要做稳定性测试的话,最好还是挂个云硬盘,防止稳定性过程中磁盘不够用的情况。
  3. 最好分配的做性能测试的机器在比较空闲的宿主机上,防止因为宿主机压力较大导致资源竞争,影响性能测试结果。
Q:怎么评估性能测试环境和线上环境的差异

A: 这是个比较难做的事情

  1. 当性能测试环境的云主机和线上配置一致时,我们可以根据性能环境的测试数据推测出线上单台应用服务器的性能情况,但是因为受限于宿主机、依赖服务性能的差异,这个数据也不是绝对准确的。
  2. 不能简单的认为集群的性能就是单台的性能 * 机器数目,因为很多情况下,后端服务可能在机器数目扩展到一定程度时首先成为瓶颈点。评估集群性能的做法还是应该先评估后端依赖服务的性能。


关于测试数据

Q:测试数据是什么

A: 测试数据分为两部分:铺底数据和测试数据,也可以从数据库中的数据以及测试脚本需要的参数数据这个角度划分。不同的测试数据对性能有一定的影响,比如说一些索引问题,我们在数据量充足的情况很容易发现,而不同的参数数据也有一定的影响,多人购买一件商品的性能和购买多件商品的性能就有较大的差异。

Q:准备测试数据时需要了解什么

A:

  1. 针对铺底数据:需要了解目前线上环境的数据量有多少,需要对重要的表的数据量进行统计分析;需要了解重要表增加幅度,例如电商相关的订单表每周或者每月的增加多少,我们基于目前的数据量以及增加速度预设半年之后的数据。
  2. 针对测试参数数据:需要简单了解接口的实现逻辑,例如可以确认
    1. 是否有缓存,缓存大小为多少:这决定准备的数据量多少,如果准备的测试数据较少,全部被缓存住,可能会使得测试结果存在不真实性
    2. 是否有热点数据相关的操作:比如说所有用户秒杀同一件商品,和用户随机购买多件商品的性能是不一样的,这种情况下,极端场景和常规场景都应该覆盖到
    3. 不同类型的数据走的逻辑存在差异时:购买普通商品和购买参与特卖活动的商品性能也是有差异的,因为代码路径存在差异,可能在你未覆盖的地方就存在问题,所以我们通过测试数据的多样化提高性能测试代码覆盖率
Q:需要准备多少的测试数据

A:

  1. 已上线的产品:我们根据测试环境的数据库性能和线上环境的对比,等比例的预估数据量的多少,例如线上环境的内存是测试环境的6倍,磁盘是线上环境的4倍,那我们可能就预估测试环境是线上环境的1/6。除此之外,还需要考虑,线上主机的增长幅度,最好在现有的基础上,增加半年的数据量作为最终的测试数据量
  2. 未上线的产品:一般情况下,我们就预估下重要的铺底数据:例如对于电商产品来说,预设商品信息、用户信息等,订单等数据通过测试时产生的数据进行铺底。
Q:不同量的测试数据对测试结果有多少影响

A: 测试数据准备不完善,可能会导致隐藏的性能问题发现不了,例如索引问题,未覆盖到的代码行隐藏的性能问题等,完善的测试数据,提升性能测试代码覆盖率,发现更多潜在的性能问题。


关于测试场景和类型

Q:要测试哪些东西

A: 一般测试哪些东西都是由提测人员决定的,但是在对一个新产品做系统的性能测试的时候,也是有些原则需要参考的:

一般测试哪些东西和我们的提测目标息息相关,在测试之前需要产品方明确,我的测试目标有哪些?

  1. 为了测试重要接口是否存在性能问题?
  2. 为了测试该接口的性能是否满足预期(需要给定预期的TPS和响应时间)?
  3. 为了了解这个接口的最大TPS是多少,性能拐点在哪儿?
  4. 为了测试系统是否稳定?
  5. 为了评估这个页面的PV有多少?
  6. 为了评估整个系统能支撑都少UV?

不同的测试目标,对应不同的测试内容和测试类型:

  1. 以发现性能问题为目标:测试场景选取重要的单接口测试,需要进行压力测试、性能测试、负载测试
  2. 以测试是否满足预期为目标:测试场景为提测接口,需要进行性能测试、负载测试
  3. 以了解接口TPS峰值为目标:需要选取重要接口,进行性能测试、负载测试
  4. 以测试系统是否稳定为目标:需要依据线上真实请求比例,设计混合场景,进行稳定测试
  5. 以评估页面PV为目标:需要基于该页面的所有接口,设计单页面场景,进行性能测试、负载测试
  6. 以评估系统能支撑的UV为目标:需要基于真实的用户业务流,设计混合场景,进行性能测试、负载测试
Q:如何提取重要的测试点

A: 针对单接口来说,提取原则如下:

  1. 重要核心业务接口,例如电商类产品的购买、支付接口,社交类的发表博客、帖子等接口
  2. 访问频率比较高的接口,例如电商类促销活动时,活动页、商品详情页等接口,以及实时获取用户推送信息等接口
  3. 逻辑实现较为复杂的接口
  4. 有复杂的数据库操作,较为频繁的磁盘读写等操作的功能点
  5. 基于Nginx日志分析得到的top url等
Q:组合场景如何设计?混合场景如何设计?

A:

  • 组合场景

通常情况下,系统的业务逻辑是比较复杂的,业务和业务之间存在先后顺序、或某种关系,或串行或并行。所以,组合场景是针对具有一定先后顺序或逻辑关系的业务进行特定场景的覆盖。

组合场景测试目标:在较复杂的业务逻辑或场景下,暴露出应用层性能问题或数据库问题等。

案例:

某购物类产品,用户典型的组合场景是,用户浏览产品、加入购物车、下单、付款。

  • 混合场景

线上系统的用户类型、操作分布、请求达到率都是实时变动的,如果进行精确的建模模拟将是一个非常复杂且困难的事情。因此,混合场景是尝试对系统进行特定切面的覆盖。基于线上用户操作进行数据统计,为被测系统建立测试模型,进一步接近真实的来模拟线上实际情况。

混合场景的设计可以分为两种:

一种是简单的朴素模型,一般用来进行稳定性,方法:基于线上一个星期或一天的日志进行分析统计,获取典型业务和业务配比,进行测试模拟,测试接口来源于我们单接口、组合接口测试时的脚本

案例: 博客产品:85%的用户在查看博客,1.5%的用户在写博客,0.5%在发表评论,5%在获取博文列表,4%在获取动态信息,4%在查看消息中心 电商类产品:80%在查看首页、商品详情页、活动页,2%在进行加入购物车、购物车商品变更、购物车支付等,3%在进行单个商品的下单、支付,15%在查看我的订单、订单详情、我的优惠券等。

一种以容量测试为目标的模型:该模型基于真实的统计数据,需要知道几种典型的用户业务流,同时在线的UV数以及不同页面的跳出比例等,设计出如下的用户模型:


关于测试脚本(针对Grinder测试工具)

Q:Post请求需要的注意点

A: 如果发送的post请求后,返回内容错误,且后端错误提示没有参数过去,请检查是否配置请求Header:Content-Type,包含如下类型:

  • NVPair('Content-Type','application/x-www-form-urlencoded; charset=UTF-8'),最常见的模式
  • NVPair('Content-Type','multipart/form-data'),用于上传附件的表单
Q:如何处理中文参数

A:

  • 读文件:

python代码如下:

import codecs

file = "keywords.txt"
infile = codecs.open(file,encoding='utf-8')
keywords = []
for line in infile.readlines():

    keywords.append(line.strip())

infile.close()
  • 输出时:常用的:print string.encode('utf-8')
Q:排除测试脚本中的性能问题

A: 不要在test.wrap的方法中做太多的事情,包含请求参数的准备和返回数据的复杂验证等。最好的情况是test中wrap的方法仅有一个http request请求。

Q:如何验证请求是否正确执行,如果失败设置为error

A: 正常200的请求,需要验证返回码是否是200,以及返回的内容是否包含异常信息或者是否包含想要的相关信息。

示例代码:

result = request806.POST(url, param,(NVPair('Cookie', session),))

#获取返回数据,前提是为使用gzip压缩
data = result.getText().encode('utf-8')

if(result.getStatusCode() != 200 or data.find('error') >= 0):
     #如此设置,grinder为认为该请求是失败请求
     grinder.getStatistics().getForCurrentTest().setSuccess(False)
     logger.error(data)
Q:如何验证302请求是否正确执行

A: 获取response中的header信息进行验证

示例代码:

    result = request808.POST(url8, params)
    if(result.getStatusCode() != 302 or result.getHeader('Location').find('pay_success_n') == -1 ):
     grinder.getStatistics().getForCurrentTest().setSuccess(False)
     infos = 'wyb_callback : ' + params + ' : ' + data
     logger.error(infos.encode('utf-8'))
Q:获取请求返回内容中是乱码,怎么办

A: 首先判读请求header中是否设置:NVPair('Accept-Encoding','gzip, deflate'),然后确认下nginx和tomcat端是否配置gzip压缩;同样可以通过返回的response header中判断返回内容是否有gzip压缩,可以通过下面的代码获取返回数据。

示例代码:

from java.io import BufferedReader,InputStreamReader
from java.util.zip import GZIPInputStream

def getData(self,result): 
    acceptEncoding=str(result.getHeader('Content-Encoding'));
    data = ''
    if (acceptEncoding.lower().find('gzip') > -1):
        reader = BufferedReader(InputStreamReader(GZIPInputStream(result.getInputStream())))
        for chunk in iter(lambda:reader.readLine(),None):
            data += chunk
    else:
        reader = BufferedReader(InputStreamReader(result.getInputStream()))
        for chunk in iter(lambda:reader.readLine(),None):
            data += chunk 

    return data


网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者侯本文授权发布。