一次文件类/图片类性能测试总结

对一些概念的理解和思路

1.基准场景,指单线程或少量线程1次性对单接口进行测试,然后将测试结果作为基准数据,为后面制定并发数提供决策数据。

有几个作用:

1)  验证测试脚本及测试参数的正确性,同时也可以验证脚本数据是否能够支持重复性测试等;

2)  通过少量线程访问系统获取结果数据,作为对比参考基准;

3)  根据测试结果,初步盘点可能成为系统瓶颈的场景,并决定是否进行后续的测试;

2.单接口负载场景,通过模拟多线程对单接口进行负载(压力从字面意思来讲就是给某物一定的,比如用手按压桌子等。压力测试就是给被测系统不断的加压(负载),测试系统的各项性能指标。直到系统的某一些性能指标达到了瓶颈为止,比如CPU的使用率达到了100%。这个不断给系统施加压力的过程,就叫做压力测试。然后,负载测试就是在系统性能指标已经有一项或几项达到了瓶颈的情况下,让它继续运行一段时间,测试系统运行过程中各项性能指标。这种在已经达到了最大负载(压力)时进行的测试叫负载测试。最后,总的来说,压力测试就是寻找系统性能瓶颈的,负载测试就是测试系统在处于瓶颈的状态的运行情况,比如响应时间是否延长等。)测试。

3.混合场景负载测试,是为了最大程度模拟用户真实操作。真实的线上操作不只有单接口的操作,一定是多业务同时进行,比如张三在浏览商品,李四在添加购物车等。

4.性能测试结果,主要查看的指标响应时间、tps(在jmeter中,大多数情况(未有错误时)下,Throughput吞吐率被认为等于TPS)、cpu占用率、错误率

5. 同步定时器,可以实现绝对的并发,持续测试时,会一直保持并发数为想要的值,比如一次请求10个并发,会等这10个并发处理全部结束后,再次发起10个并发,但实际上程序处理时基本不存在持续的绝对并发。要看需求要求,这种一般用在混合场景测试,比如10个人登录后,要求测试10个人同时添加购物车操作时,可以使用同步定时器。但如果对单接口测试时,需求没有要求下,无需使用同步定时器,以达到真实请求的效果。(例如开发的需求是,他想知道同时启用几个线程,请求ai识别接口,响应不会超时,报错等,这个时候是无需使用同步定时器,才能真实模拟他想要的效果)

例子:Ai接口性能测试(传入羊只的图片过去,然后获得图片中羊的重量和只数,还有原图被标注羊后返回)

基准场景测试,

1个线程请求,平均响应时间大概在3s左右;

3个线程1s内并发请求,平均响应时间大概在4s;

5个线程1s内并发请求,平均响应时间大概在6s;

10个线程1s内并发请求,平均响应时间大概在12s;

20个线程1s内并发请求,平均响应时间大概在26s;

30个线程1s内并发请求,平均响应时间大概在42s;

         我们程序可接受的响应时间在30s内,是定时任务执行,所以,30s是我们一个参考值,通过基准场景,可以看出,在20个并发接近30s,但是系统cpu没有达到很大压力,这个时候,可以以响应时间做一个基准,去测试我们想要的合适的并发数;

对20个并发,进行负载测试,持续10分钟,观察响应时间,经过测试结果如下


通过报告,可以看出,95%的接口响应时间都小于30s,5%的接口响应时间可能会大于30s,跟开发讨论,20个并发基本是满足我们系统的最大并发数了;这个时候,20个并发可以多测试几轮10分钟,15分钟,以保证数据的准确性。

在来看下tps,因为请求都没有报错,这里就默认吞吐量等于tps,通过不断增加线程数,每个线程持续10分钟压测,得到如下图(横轴是线程数,纵轴是吞吐量)


由此看出,11个并发是1个拐点,大于11个并发,ai系统处理请求的能力开始下降。

至此,目前本次性能测试结束。

在测试过程中遇到的问题总结

1. 文件上传或者是图片上传时,大多数时候都需要转换成base64,这个时候,如果直接使用jmeter的http请求参数会很大,就会出现卡死的情况。百度尝试各种方法后,发现可是使用java请求来避免这个问题。

新增java请求步骤如下:

1.1 使用idea新建一个maven工程


1.2新增后,copy代码,可以百度,这次的代码如下:

import cn.hutool.http.HttpUtil;

import com.alibaba.fastjson.JSON;

import org.apache.jmeter.config.Arguments;

import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;

import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;

import org.apache.jmeter.samplers.SampleResult;

import sun.misc.BASE64Encoder;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.HashMap;

import java.util.Map;

public class AiTest extends AbstractJavaSamplerClient {

@Override

public Arguments getDefaultParameters() {

//设置请求参数名

       Arguments arguments = new Arguments();

        arguments.addArgument("url","");//请求的接口地址

        arguments.addArgument("filePath","");//传入图片本地地址

        return arguments;

}

@Override

public SampleResult runTest(JavaSamplerContext javaSamplerContext) {

//获取传入的请求参数

        String url = javaSamplerContext.getParameter("url");

        String filePath = javaSamplerContext.getParameter("filePath");

        SampleResult sr = new SampleResult();// 初始化一个SampleResult

        sr.setSamplerData("请求参数url:" + url + "\n请求参数filePath:" + filePath);// 参数放进sr

        try {

            sr.sampleStart(); // jmeter 开始统计响应时间标识

            String file=getImgData(filePath);//将通过地址获取图片并转换为base64

            Map<String, Object> param = new HashMap<>();

            param.put("file", file);

            param.put("deviceId", 1);

            String paramStr = JSON.toJSONString(param);

            String post = HttpUtil.post(url, paramStr);

            sr.setResponseData("响应结果是:" + post);// 结果放入sr,并设置字符集

            sr.setDataType(SampleResult.TEXT);

            sr.setSuccessful(true); // 设置响应执行成功

        } catch (Throwable e) {

            sr.setSuccessful(false);// 有异常,执行失败

            e.printStackTrace();

     } finally {

            sr.sampleEnd(); // jmeter 结束统计响应时间标识

        }return sr;}

// 图片转化成base64字符串

    public static String getImageStr(String file) {

// 将图片文件转化为字节数组字符串,并对其进行Base64编码处理

        String imgFile = file; // 待处理的图片

        InputStream in = null;

        byte[] data = null;

// 读取图片字节数组

        try {in =new FileInputStream(imgFile);

data = new byte[in.available()];

in.read(data);

in.close();}catch (IOException e){

            e.printStackTrace();

}

// 对字节数组Base64编码

       BASE64Encoder encoder = new BASE64Encoder();

       return encoder.encode(data);// 返回Base64编码过的字节数组字符串

    }

public static String getImgData(String filePath) {

// windows平台需要转换‘’该转义符

        String filedata = filePath.replaceAll("%2F%2F", "%2F");

// 因编码出来不是在一行上,所以需要处理掉换行符

        String data =getImageStr(filedata).replaceAll("\n", "");

        return data;

}

public static void main(String[] args){

// 本地测试

        String url="http://goatapi-test.shuyixin.cn/syx/weighingdata";

        String filePath="D:\\work\\项目\\大足黑山羊\\ai接口性能测试\\goat\\310.jpg";

        String file=getImgData(filePath);

        Map<String, Object> param = new HashMap<>();

        param.put("file", file);

        param.put("deviceId", 1);

        String paramStr = JSON.toJSONString(param);

        String post = HttpUtil.post(url, paramStr);

        System.out.println("响应结果:"+post);

}}

1.3 导入需要的jar包


导入完成后,会在这里看到


1.4 运行main方法,测试下


运行后,若发现会报错


百度后,需要引slf4j的包,这个时候,在pom里面添加


修改pom文件后,会提示如下,点击import Changes


完成后,会看到


再次运行main测试一下,可以正常请求成功;

1.5 测试成功后,导出jar包

有两种方式,1种是使用maven工具,1种是手动打包,手动打包

maven打包


手动打包


移除掉jmeter相关的包


复制jar包到jmeter,/lib/ext 目录下


启动jmeter,新增一个Java请求,选择自己创建的类名,填写参数即可


2. 持续压测时,设置了几个线程数,就从csv里面重复请求几个线程,而没有往下走。


后面通过百度和不断的尝试,发现是java请求打包的问题,因为打包时没有去掉jmeter相关的核心包导致这个问题,后面重新打包,问题解决。

3.持续压测时,需要大量的不同的图片。

使用java,在图片上加水印的方式,可以批量生成大量不同的图片。可以在刚刚新建的项目里面,新增一个类,copy代码如下(百度也可以),然后执行main方法(需要修改的只有原图片地址和目标图片地址):

package com.zds.autotest.agriculture.process;

import lombok.Getter;

import lombok.Setter;

import lombok.extern.slf4j.Slf4j;

import java.awt.Color;

import java.awt.Font;

import java.awt.Graphics2D;

import java.awt.Image;

import java.awt.image.BufferedImage;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

import javax.imageio.ImageIO;

@Slf4j

public class WaterMarkUtils {


/**

         *编辑图片,往指定位置添加文字

         *&#64;param srcImgPath    :源图片路径

         *&#64;param targetImgPath :保存图片路径

         *&#64;param list          :文字集合

         */

        public static void writeImage(String srcImgPath, String targetImgPath, List list) {

            FileOutputStream outImgStream =null;

            try{

//读取原图片信息

                File srcImgFile = new File(srcImgPath);//得到文件

                Image srcImg = ImageIO.read(srcImgFile);//文件转化为图片

                int srcImgWidth = srcImg.getWidth(null);//获取图片的宽

                int srcImgHeight = srcImg.getHeight(null);//获取图片的高

                //添加文字:

                BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);

                Graphics2D g = bufImg.createGraphics();

                g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);

                for(ImageDTO imgDTO : list) {

                    g.setColor(imgDTO.getColor());                                  //根据图片的背景设置水印颜色

                    g.setFont(imgDTO.getFont());                                    //设置字体

                    g.drawString(imgDTO.getText(), imgDTO.getX(), imgDTO.getY());   //画出水印

                }

                g.dispose();

// 输出图片

                outImgStream = new FileOutputStream(targetImgPath);

                ImageIO.write(bufImg,"jpg", outImgStream);

} catch (Exception e) {

log.error("==== 系统异常::{} ====",e);

}finally {

try {if (null != outImgStream){

      outImgStream.flush();

      outImgStream.close();

} }catch (IOException e) {

                    e.printStackTrace();}}

 }

/**

         *创建ImageDTO, 每一个对象,代表在该图片中要插入的一段文字内容:

         *&#64;param text  :文本内容;

         *&#64;param color : 字体颜色(前三位)和透明度(第4位,值越小,越透明);

         *&#64;param font  :字体的样式和字体大小;

         *&#64;param x     :当前字体在该图片位置的横坐标;

         *&#64;param y     :当前字体在该图片位置的纵坐标;

         *&#64;return

         */

        private static ImageDTO createImageDTO(String text,Color color,Font font,int x,int y){

            ImageDTO imageDTO =new ImageDTO();

            imageDTO.setText(text);

            imageDTO.setColor(color);

            imageDTO.setFont(font);

            imageDTO.setX(x);

            imageDTO.setY(y);

            return  imageDTO;

}

/**

         * main方法:

         *&#64;param args

         */

        public static void main(String[] args) {

//todo 自己真实的本地地址:

            String srcImgPath="D:\\work\\项目\\大足黑山羊\\ai接口性能测试\\goat\\1000.jpg";    //源图片地址

            String tarImgPath="D:\\work\\项目\\大足黑山羊\\ai接口性能测试\\goat\\10.jpg";   //目标图片的地址

            for (int i=14078;i<=16720;i++){

                tarImgPath="D:\\work\\项目\\大足黑山羊\\ai接口性能测试\\goat\\"+i+".jpg";   //目标图片的地址

                //获取数据集合;

                ArrayList<ImageDTO> list = new ArrayList<>();

                list.add(createImageDTO(String.valueOf(i),new Color(255,59,48),new Font("微软雅黑", Font.PLAIN, 36), 350, 366));

//操作图片:

                WaterMarkUtils.writeImage(srcImgPath, tarImgPath, list);

}

log.info("生成完成");

//这句代码,自己项目中可以不用加,在这里防止main方法报错的;

//            System.exit(0);

        }

}

/**

 *存放文本内容的类

 */

@Setter

@Getter

class ImageDTO{

//文字内容

    private String text;

//字体颜色和透明度

    private Color color;

//字体和大小

    private Font font;

//所在图片的x坐标

    private int x;

//所在图片的y坐标

    private int y;

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • 注:二八定律:80%员工在20%时间登录;80%员工在使用20%的功能 一、什么是性能测试 概念:性能测试是模拟多...
    MAVIS_42bf阅读 5,668评论 0 0
  • 1、背景 开放平台新对接并对外提供智能认证的服务,服务提供方应该提供的相关的性能能力,但由于涉及到开放平台接入方服...
    Hoututu阅读 486评论 0 0
  • 原文见:在路上的博客:服务端性能测试-工具篇[https://www.ontheway.cool/posts/42...
    测试在路上阅读 1,031评论 0 0
  • 在众多类型的软件测试中,性能测试以软件响应速度为测试目标,尤其是在较短时间内大量并发用户的访问时,软件是否具有良好...
    robot_test_boy阅读 1,459评论 0 4
  • 线上业务压测的核心要素 做到 5 个一样要达成精准衡量业务承接能力的目标,业务压测就需要做到 5 个一样: 一样的...
    我为峰2014阅读 5,248评论 3 37