写点什么

插上 NIO 翅膀,FunTester 飞上天

用户头像
FunTester
关注
发布于: 4 小时前

在前段时间对性能测试框架对比的文章中,我又重新学习了Java NIO知识的学习,又发掘了一项 FunTester 优化,说干就干,现在就行动起来。


首先呢,先复习一下关于统计 QPS 中用到到一个请求模型。在之前的两篇讨论性能测试误差的文章性能测试误差分析文字版-上性能测试误差分析文字版-下中,我画了一个简单的请求时间模型。

计算模型

如图所示,这是单个线程单个请求的耗时简易模型,分成三部分:请求前(对应before)、请求与响应(对应request and response)和请求后(对应after)。其中T代表三个部分的总时间,rt代表了请求与响应的时间。


Java NIO

Java NIO 有两种解释:一种叫非阻塞 IO(Non-blocking I/O),另一种也叫新的 IO(New I/O),其实是同一个概念。NIO 是一种基于通道和缓冲区的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存(区别于 JVM 的运行时数据区),然后通过一个存储在 java 堆里面的 DirectByteBuffer 对象作为这块内存的直接引用进行操作。这样能在一些场景显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。


以上内容摘要,其实我的理解也不透彻。PS:我更建议有能力的搜一搜,了解一下也是好的。


下面我分享一下Java NIOHTTP协议接口测试中的应用。

缘由

在上图第二部分中,请求和响应占据了整个部分。如果我们将这部分再细分,那么可以分成三个部分:发出请求、等待响应、接收响应。Java NIO在接口测试中的应用就在等待响应和接收响应这一部分。如果我们使用一种技术,将发出请求之后,等待响应和接收响应这个过程交给另外的线程处理,又不影响两者之间的关系,那么我们就可以不断地发出请求,提高客户端性能而又不影响我们接收响应,进行业务验证。


对于那些响应时间比较长的接口来说。这样的处理结果能够极大的提升客户端发送请求的速率。对于线程数一定的情况下,由单个客户端发起的压力也会成倍的增加。经过本人在本地进行单线程模拟测试。(这个倍数大约在 30 倍左右。可见Java NIO的性能提升有多强。当然在实际的更大压力的性能测试中,这个倍数会降低很多。)

HttpClient 应用

HttpAsyncClient 则使用 Java NIO 的异步非阻塞事件驱动 I/O 模型,实现了真正意义的异步调用,使用 HttpAsyncClient 我们需要引入其专门的包


之前我有写过文章:HTTP异步连接池和多线程实践,只不过现在在看当时显得特别的字呢,对于 http client 异步客户端也有点儿理解不透彻,特别是对于 callback 函数的应用。


核心方法如下:


    @Override    public Future<HttpResponse> execute(            final HttpUriRequest request,            final FutureCallback<HttpResponse> callback) {        return execute(request, HttpClientContext.create(), callback);    }
复制代码


第一个是参数请求对象,第二个参数是回调函数。其中我之前常用的请求对象org.apache.http.client.methods.HttpRequestBase,具体实现代码摘要public abstract class HttpRequestBase extends AbstractExecutionAwareRequest implements HttpUriRequest, Configurable


下面是我经过一些资料的查证,重新写了一下。Http client 异步客户端的使用方法的封装。

不管不顾

这个方法只负责把请求发出去,至于响应一律不管。这里据我查证,callback如果传null的话,在处理响应的时候会直接释放连接等相关资源。


    /**     * 异步发送请求     *     * @param request     */    public static void executeSync(HttpRequestBase request) {        ClientManage.httpAsyncClient.execute(request, null);    }
复制代码

异步打印日志

通过一个简单的日志打印功能实现FutureCallback,来实现异步响应结果的解析和日志打印功能。


    /**     * 异步请求,打印日志     *     * @param request     * @param response     */    public static void executeSyncWithLog(HttpRequestBase request) {        ClientManage.httpAsyncClient.execute(request, logCallback);    }

/** * 异步请求打印日志的callback */ public static final FutureCallback<HttpResponse> logCallback = new FutureCallback<HttpResponse>() { @Override public void completed(HttpResponse httpResponse) { HttpEntity entity = httpResponse.getEntity(); String content = getContent(entity); logger.info("响应结果:{}", content); }
@Override public void failed(Exception e) { logger.warn("响应失败", e); }
@Override public void cancelled() { logger.warn("取消执行"); } };
复制代码

异步解析响应

这里我引入了第二个参数com.alibaba.fastjson.JSONObject,用来保存解析响应,之所以加上这个,用来异步保存响应结果,用来业务验证。



/** * 异步请求,返回响应,引入第二个参数{@link JSONObject} * * @param request * @param response */ public static void executeSyncWithResponse(HttpRequestBase request, JSONObject response) { ClientManage.httpAsyncClient.execute(request, new FunTester(response)); }

/** * 异步请求,异步解析响应的FutureCallback实现类 */ private static class FunTester implements FutureCallback<HttpResponse> {
public FunTester(JSONObject response) { this.response = response; }
JSONObject response;
@Override public void completed(HttpResponse result) { HttpEntity entity = result.getEntity(); String content = getContent(entity); response = JSON.parseObject(content); }
@Override public void failed(Exception e) { logger.warn("响应失败", e); }
@Override public void cancelled() { logger.warn("取消执行"); }
}
复制代码


  • httpAsyncClientHttpClient性能对比,以及这三个方法的性能比较。后续我会进行对比测试。原因是本地服务响应太快,无法体现差异。


Have Fun ~ Tester !





点击阅读阅文,查看 FunTester 历史原创集合

发布于: 4 小时前阅读数: 2
用户头像

FunTester

关注

公众号:FunTester,Have Fun, Tester! 2020.10.20 加入

Have Fun,Tester!

评论

发布
暂无评论
插上NIO翅膀,FunTester飞上天