HTTP接口性能测试中池化实践

今天我们分享一下HTTP请求的池化实践,分为两个org.apache.http.client.methods.HttpGet和org.apache.http.client.methods.HttpPost,由于代码雷同较多,会重点分享GET请求的实践,最后会附上POST请求的代码。之所以没有选用GenericKeyedObjectPool,因为这个实现类的代码注释中已经标明了可能存在性能瓶颈,我计划先测试其性能之后在做实践。

在这里插入图片描述

池化工具类

这里我将池化工厂类写成了内部静态类的形式,这样可以显得代码中的Java文件比较少,比较整洁。在工具类(包含池化和工程类)中,我并没有重写destroyObject方法,原因是现在是写框架部分,如果需要对HTTP请求对象进行处理,比如清除token信息等操作,可以写到业务类中,如果框架做了,那么适应性就比较差了。根据我的实践经验,大部分时候我们只需要重置很少一部分信息,请求头里面大多数header都是可以原封不到留到对象中,不会有任何影响,

package com.funtester.funpool


import com.funtester.config.PoolConstant
import org.apache.commons.pool2.BasePooledObjectFactory
import org.apache.commons.pool2.PooledObject
import org.apache.commons.pool2.impl.DefaultPooledObject
import org.apache.commons.pool2.impl.GenericObjectPool
import org.apache.commons.pool2.impl.GenericObjectPoolConfig
import org.apache.http.client.methods.HttpGet
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger

class HttpGetPool extends PoolConstant {

    private static final Logger logger = LogManager.getLogger(HttpGetPool.class);

    private static GenericObjectPool<HttpGet> pool

    static def init() {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(MAX);
        poolConfig.setMinIdle(MIN_IDLE);
        poolConfig.setMaxIdle(MAX_IDLE);
        poolConfig.setMaxWaitMillis(MAX_WAIT_TIME);
        poolConfig.setMinEvictableIdleTimeMillis(MAX_IDLE_TIME);
        pool = new GenericObjectPool<>(new FunTester(), poolConfig);
    }


    /**
     * 获取{@link org.apache.http.client.methods.HttpGet}对象
     * @return
     */
    static HttpGet get() {
        try {
            return pool.borrowObject()
        } catch (e) {
            logger.warn("获取${HttpGet.class} 失败", e)
            new HttpGet()
        }
    }

    /**
     * 归还{@link org.apache.http.client.methods.HttpGet}对象
     * @param httpGet
     * @return
     */
    static def back(HttpGet httpGet) {
        pool.returnObject(httpGet)
    }

    /**
     * 执行任务
     * @param closure
     * @return
     */
    def execute(Closure closure) {
        def get = get()
        try {
            closure(get)
        } catch (e) {
            logger.warn("执行任务失败", e)
        } finally {
            back(get)
        }

    }

    private static class FunTester extends BasePooledObjectFactory<HttpGet> {

        @Override
        HttpGet create() throws Exception {
            return new HttpGet()
        }

        @Override
        PooledObject<HttpGet> wrap(HttpGet obj) {
            return new DefaultPooledObject<HttpGet>(obj)
        }
    }
}

实践

服务端

依旧采用了我最喜欢的moco_FunTester框架,为了验证URL已经会替换我多写了几个接口,代码如下:

package com.mocofun.moco.main

import com.funtester.utils.ArgsUtil
import com.mocofun.moco.MocoServer

class Share extends MocoServer {

    static void main(String[] args) {
        def util = new ArgsUtil(args)
        def server = getServerNoLog(util.getIntOrdefault(0, 12345))
        server.get(urlOnly("/get")).response(jsonRes(getJson("msg=get请求", "code=0")))
        server.get(urlOnly("/get1")).response(jsonRes(getJson("msg=get1请求", "code=1")))
        server.get(urlOnly("/get2")).response(jsonRes(getJson("msg=get2请求", "code=2")))
        server.get(urlOnly("/get3")).response(jsonRes(getJson("msg=get2请求", "code=3")))
        server.get(urlOnly("/get4")).response(jsonRes(getJson("msg=get2请求", "code=4")))
        server.response("Have Fun ~ Tester !")
        def run = run(server)
        waitForKey("FunTester")
        run.stop()
    }
}

客户端

这里只测试GET请求,大家可以注意看一下我注释掉的一行代码// get.setURI(null)就是在将GET请求对象归还对象池之前,做不做这个操作对实际结果并无影响。另外我自己用了非池化技术实现的请求方法也做了同样的测试和验证功能。

package com.funtest.groovytest


import com.funtester.frame.SourceCode
import com.funtester.frame.execute.ThreadPoolUtil
import com.funtester.funpool.HttpGetPool
import com.funtester.httpclient.FunHttp

class PoolTest extends SourceCode {

    public static void main(String[] args) {
        def url = "http://localhost:12345/get"
        HttpGetPool.init()
        100.times {
            fun {
                Res(url+getRandomInt(4))
                Res2(url+getRandomInt(4))
            }
        }
    }

    /**
     * 使用池化技术
     * @param url
     * @return
     */
    static def Res(def url) {
        def get = HttpGetPool.get()
        get.setURI(new URI(url))
        def response = FunHttp.getHttpResponse(get)
//        get.setURI(null)
        HttpGetPool.back(get)
        def integer = response.getInteger("code")
        if (!url.endsWith(integer as String)) {
            fail()
        }
    }

    static def Res2(def url) {
        def response = FunHttp.getHttpResponse(FunHttp.getHttpGet(url))
        def integer = response.getInteger("code")
        if (!url.endsWith(integer as String)) {
            fail()
        }

    }

}

控制台信息:

21:22:05.595 main 
  ###### #     #  #    # ####### ######  #####  ####### ######  #####
  #      #     #  ##   #    #    #       #         #    #       #    #
  ####   #     #  # #  #    #    ####    #####     #    ####    #####
  #      #     #  #  # #    #    #            #    #    #       #   #
  #       #####   #    #    #    ######  #####     #    ######  #    #

21:22:06.116 Deamon 守护线程开启!
21:22:06.560 F-8  请求uri:http://localhost:12345/get3 , 耗时:418 ms , HTTPcode: 200
21:22:06.560 F-7  请求uri:http://localhost:12345/get4 , 耗时:418 ms , HTTPcode: 200
…………………………………………………………省略部分日志…………………………………………………………………………………………
21:22:06.575 F-1  请求uri:http://localhost:12345/get3 , 耗时:2 ms , HTTPcode: 200
21:22:06.575 F-7  请求uri:http://localhost:12345/get2 , 耗时:1 ms , HTTPcode: 200
21:22:06.727 main 异步线程池等待执行1次耗时:607 ms
21:22:07.157 Deamon 异步线程池关闭!

进程已结束,退出代码0

可以看到日志中并没有报错,每个线程发起第一次请求的时候都会因为创建连接而耗时较长,后面的请求连接复用之后就非常快了。异步线程池默认大小是8,F-2代表第二个线程,文中日志不全。

在这里插入图片描述

最后: 可以在公众号:伤心的辣条 ! 自行领取一份216页软件测试工程师面试宝典文档资料【免费的】。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

学习不要孤军奋战,最好是能抱团取暖,相互成就一起成长,群众效应的效果是非常强大的,大家一起学习,一起打卡,会更有学习动力,也更能坚持下去。你可以加入我们的测试技术交流扣扣群:914172719(里面有各种软件测试资源和技术讨论)

喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值