JDK Httpclient 使用和性能测试
Httpclient 使用和性能测试
上篇,通过简介和架构图,我们对HttpClient有了初步的了解。
本篇我们展示HttpClient的简单使用,同时为了说明httpclient的使用性能,我们将Httpclient的同步和异步模式与apache的Httpclient4作比较。。
1. HttpClient示例代码
以下基本是官方示例,分别展示了如何使用Get和Post请求。
HttpClient client = HttpClient.newBuilder()
.version(Version.HTTP_1_1) //可以手动指定客户端的版本,如果不指定,那么默认是Http2
.followRedirects(Redirect.NORMAL) //设置重定向策略
.connectTimeout(Duration.ofSeconds(20)) //连接超时时间
.proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80))) //代理地址设置
.authenticator(Authenticator.getDefault())
//.executor(Executors.newFixedThreadPoolExecutor(8)) //可手动配置线程池
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://foo.com/")) //设置url地址
.GET()
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); //同步发送
System.out.println(response.statusCode()); //打印响应状态码
System.out.println(response.body());
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://foo.com/"))
.timeout(Duration.ofMinutes(2)) //设置连接超时时间
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofFile(Paths.get("file.json"))) //设置请求体来源
.build();
client.sendAsync(request, BodyHandlers.ofString()) //异步发送
.thenApply(HttpResponse::body) //发送结束打印响应体
.thenAccept(System.out::println);
可以看到,应用编写的代码相对流畅自然。不过,也有几个注意点
- Http连接池不支持手动配置,默认是无限复用的
- 重试次数不支持手动配置
- 不指定Http客户端或请求的版本,会默认使用Http2模式进行连接,受挫后会进行降级
- 请求的同步发送模式(send)实际上会后台另开线程
短短的几行代码只是实现了功能,那么,它的性能如何呢?我们把它和业界标杆——Apache 的HttpClient作对比。
2. 服务器测试代码编写
为了简便,使用node.js的http模块运行一个简易的服务器。该服务器驻守在8080端口,每收到一个请求,停留500ms后返回响应。
let http = require("http")
let server = http.createServer()
server.addListener("request", (req, res) => {
if (req.url.startsWith("/")) {
//接到任意请求,停留0.5秒后返回
setTimeout(() => {
res.end("haha")
}, 500)
}
}
)
server.listen(8080, () => console.log("启动成功!"))
使用node运行该js文件,提示已启动成功
3. JDK httpclient 和apache Httpclient 测试代码
首先定义公共的测试接口:
public interface Tester {
//测试参数
class TestCommand {
}
/**
* 测试主方法
* @param testCommand 测试参数
*/
void test(TestCommand testCommand) throws Exception;
/**
* 重复测试多次
* @param testName 测试名称
* @param times 测试次数
* @param testCommand 每次测试的参数
*/
default void testMultipleTimes(String testName, int times, TestCommand testCommand) throws Exception{
long startTime = System.currentTimeMillis();
System.out.printf(" ----- %s开始,共%s次 -----\n", testName, times);
for (int i = 0; i < times; i++) {
long currentStartTime = System.currentTimeMillis();
test(testCommand);
System.out.printf("第%s次测试用时:%sms\n", i + 1, (System.currentTimeMillis() - currentStartTime));
}
long usedTime = System.currentTimeMillis() - startTime;
System.out.printf("%s次测试共用时:%sms,平均用时:%sms\n", times, usedTime, usedTime / times);
}
}
定义测试类,包含三个静态嵌套类,分别用作JDK httpclient的异步模式、同步模式和apache Httpclient的同步模式
public class HttpClientTester {
/** Http请求的真正测试参数*/
static class HttpTestCommand extends Tester.TestCommand {
/**目的url*/
String url;
/**单次测试请求次数*/
int requestTimes;
/**请求线程数*/
int threadCount;
public HttpTestCommand(String url, int requestTimes, int threadCount) {
this.url = url;
this.requestTimes = requestTimes;
this.threadCount = threadCount;
}
}
static class BlocklyHttpClientTester implements Tester {
@Override
public void test(TestCommand testCommand) throws Exception {
HttpTestCommand httpTestCommand = (HttpTestCommand) testCommand;
testBlockly(httpTestCommand.url, httpTestCommand.requestTimes,httpTestCommand.threadCount);
}
/**
* 使用JDK Httpclient的同步模式进行测试
* @param url 请求的url
* @param times 请求次数
* @param threadCount 开启的线程数量
* @throws ExecutionException
* @throws InterruptedException
*/
void testBlockly(String url, int times, int threadCount) throws ExecutionException, InterruptedException {
threadCount = threadCount <= 0 ? 1 : threadCount;
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
HttpClient client = HttpClient.newBuilder().build();
Callable<String> callable1 = () -> {
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
return response.body();
};
List<Future<String>> futureList1 = new ArrayList<>();
for (int i = 0; i < times; i++) {
Future<String> future1 = executorService.submit(callable1);
futureList1.add(future1);
}
for (Future<String> stringFuture : futureList1) {
//阻塞直至所有请求返回
String s = stringFuture.get();
}
executorService.shutdown();
}
}
static class NonBlocklyHttpClientTester implements Tester {
@Override
public void test(TestCommand testCommand) throws Exception {
HttpTestCommand httpTestCommand = (HttpTestCommand) testCommand;
testNonBlockly(httpTestCommand.url, httpTestCommand.requestTimes);
}
/**
* 使用JDK Httpclient的异步模式进行测试
* @param url 请求的url
* @param times 请求次数
* @throws InterruptedException
*/
void testNonBlockly(String url, int times) throws InterruptedException {
//给定16个线程,业务常用 2 * Runtime.getRuntime().availableProcessors()
ExecutorService executor = Executors.newFixedThreadPool(16);
HttpClient client = HttpClient.newBuilder()
.executor(executor)
.build();
//使用倒计时锁来保证所有请求完成
CountDownLatch countDownLatch = new CountDownLatch(times);
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build();
while (times-- >= 0) {
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.whenComplete((stringHttpResponse, throwable) -> {
if (throwable != null) {
throwable.printStackTrace();
}
if (stringHttpResponse != null) {
stringHttpResponse.body();
}
countDownLatch.countDown();
});
}
//阻塞直至所有请求完成
countDownLatch.await();
executor.shutdown();
}
}
static class ApacheHttpClientTester implements Tester {
@Override
public void test(TestCommand testCommand) throws Exception {
HttpTestCommand httpTestCommand = (HttpTestCommand) testCommand;
testBlocklyWithApacheClient(httpTestCommand.url, httpTestCommand.requestTimes,httpTestCommand.threadCount);
}
/**
* 使用Apache HttpClient进行测试
* @param url 请求的url
* @param times 使用时长
* @param threadCount 开启的线程数量
* @throws ExecutionException
* @throws InterruptedException
*/
void testBlocklyWithApacheClient(String url, int times, int threadCount) throws ExecutionException, InterruptedException {
threadCount = threadCount <= 0 ? 1 : threadCount;
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
//设置apache Httpclient连接复用无限制,体现其最大性能
connectionManager.setDefaultMaxPerRoute(Integer.MAX_VALUE);
connectionManager.setMaxTotal(Integer.MAX_VALUE);
CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager).build();
Callable<String> callable1 = () -> {
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpClient.execute(httpGet);
return EntityUtils.toString(response.getEntity());
};
List<Future<String>> futureList1 = new ArrayList<>();
for (int i = 0; i < times; i++) {
Future<String> future1 = executorService.submit(callable1);
futureList1.add(future1);
}
for (Future<String> stringFuture : futureList1) {
//阻塞直至所有请求返回
String s = stringFuture.get();
}
executorService.shutdown();
}
}
测试的main方法:
public static void main(String[] args) {
try {
//
HttpTestCommand testCommand = new HttpTestCommand("http://localhost:8080", 200, 16);
//每个测试重复3轮,减少误差
final int testTimes = 3;
new BlocklyHttpClientTester().testMultipleTimes("JDK HttpClient同步模式测试", testTimes, testCommand);
new NonBlocklyHttpClientTester().testMultipleTimes("JDK HttpClient异步模式测试", testTimes, testCommand);
new ApacheHttpClientTester().testMultipleTimes("Apache Httpclient同步模式测试", testTimes, testCommand);
} catch (Exception e) {
e.printStackTrace();
}
}
4. 测试结果
----- JDK HttpClient同步模式测试开始,共3次 -----
第1次测试用时:4414ms
第2次测试用时:3580ms
第3次测试用时:3620ms
3次测试共用时:11620ms,平均用时:3873ms
----- JDK HttpClient异步模式测试开始,共3次 -----
第1次测试用时:568ms
第2次测试用时:595ms
第3次测试用时:579ms
3次测试共用时:1742ms,平均用时:580ms
----- Apache Httpclient同步模式测试开始,共3次 -----
第1次测试用时:3719ms
第2次测试用时:3557ms
第3次测试用时:3574ms
3次测试共用时:10851ms,平均用时:3617ms
可见,Httpclient同步模式与apacheHttpclient同步模式性能接近;异步模式由于充分利用了nio非阻塞的特性,在线程数相同的情况下,效率大幅优于同步模式。
需要注意的是,此处的“同步”“异步”并非I/O模型中的同步,而是指编程方式上的同步/异步。
5. 总结
通过以上示例代码,可以看出HttpClient具有编写流畅、性能优良的特点,也有可定制性不足的遗憾。
下一节,我们将深入客户端的构建和启动过程,接触选择器管理者这一角色,探寻它和Socket通道的交互的交互过程。
JDK Httpclient 使用和性能测试的更多相关文章
- JDK HttpClient 单次请求的生命周期
HttpClient 单次请求的生命周期 目录 HttpClient 单次请求的生命周期 1. 简述 2. uml图 3. Http连接的建立.复用和降级 3.1 调用流程及连接的建立和复用 3.2 ...
- JDK HttpClient 多重请求-响应的处理
HttpClient 多重请求-响应的处理 目录 HttpClient 多重请求-响应的处理 1. 简述 2. 请求响应流程图 3. 用户请求的复制 4. 多重请求处理概览 5. 请求.响应过滤的执行 ...
- JDK httpClient 详解(源码级分析)——概览及架构篇
1. 前言 2018年9月,伴随着java 11的发布,内置的httpclient正式登上了历史的舞台.此前,JDK内置的http工具URLConnection性能羸弱,操作繁琐,饱受诟病,也因此令如 ...
- Jmh测试JDK,CGLIB,JAVASSIST动态代理方式的性能
前言 JDK,CGLIB,JAVASSIST是常用的动态代理方式. JDK动态代理仅能对具有接口的类进行代理. CGLIB动态代理方式的目标类可以没有接口. Javassist是一个开源的分析.编辑和 ...
- 在java中使用JMH(Java Microbenchmark Harness)做性能测试
文章目录 使用JMH做性能测试 BenchmarkMode Fork和Warmup State和Scope 在java中使用JMH(Java Microbenchmark Harness)做性能测试 ...
- 小师妹学JVM之:JIT中的LogCompilation
目录 简介 LogCompilation简介 LogCompilation的使用 解析LogCompilation文件 总结 简介 我们知道在JVM中为了加快编译速度,引入了JIT即时编译的功能.那么 ...
- HashMap 的 7 种遍历方式与性能分析
前言 随着 JDK 1.8 Streams API 的发布,使得 HashMap 拥有了更多的遍历的方式,但应该选择那种遍历方式?反而成了一个问题. 本文先从 HashMap 的遍历方法讲起,然后再从 ...
- HttpURLConnection与 HttpClient 区别/性能测试对比
HttpClient是个开源框架,封装了访问http的请求头,参数,内容体,响应等等, HttpURLConnection是java的标准类,什么都没封装,用起来太原始,不方便 HttpClient实 ...
- centos7 lvm合并分区脚本初探-linux性能测试 -centos7修改网卡名字-jdk环境安装脚本-关键字查询文件-批量添加用户
1.#!/bin/bash lvmdiskscan | grep centos > /root/a.txt a=`sed -n '1p' /root/a.txt` b=`sed -n '2p' ...
随机推荐
- 02-爬取http://www.allitebooks.org/网站,获取图片url,书名,简介,作者
import requests from lxml import etree from bs4 import BeautifulSoup import json class BookSpider(ob ...
- Maven 目录结构[转载]
转载至:http://www.cnblogs.com/haippy/archive/2012/07/05/2577233.html Maven 标准目录结构 好的目录结构可以使开发人员更容易理解项目, ...
- C++11的auto自动推导类型
auto是C++11的类型推导关键字,很强大 例程看一下它的用法 #include<vector> #include<algorithm> #include<functi ...
- vue-cli2嵌入html
1.使用iframe <!-- 相对路径/绝对路径 --> <iframe src="../../../static/zsw.html"></ifra ...
- AT1980 [AGC001B] Mysterious Light 题解
# 题意:高桥 くん 有一个边长为 N 的三枚镜子构成的正三角形 , 顶点为 a, b, c. 他有一个超级步枪 , 放在 AB 段的P点上,使得 AP=X . 并沿着平行于 BC 的方向发射一道光 ...
- 一文掌握 Python 的描述符协议
描述符介绍 描述符本质就是一个新式类,在这个新式类中,至少要实现了__get__(),__set__(),__delete__()中的一个.这也被称为描述符协议. class Myclass(obje ...
- M函数目录(Power Query 之 M 语言)
2021-12-11更新 主页(选项卡) 管理列(组) 选择列 选择列Table.SelectColumns 删除列 删除列Table.RemoveColumns 删除其他列Table.SelectC ...
- Excel数据导出功能
HTML代码: <a id="aExportData" hidden><span>Export</span></a> <div ...
- hashlib加密模块主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法,HMAC消息签名(HMAC-SHA1,....)
hashlib模块 用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法 MD5 摘要输出 # ...
- 【JAVA今法修真】 第六章 天道无情,锁定乾坤
您好,我是南橘,万法仙门的掌门,刚刚从九州世界穿越到地球,因为时空乱流的影响导致我的法力全失,现在不得不通过这个平台向广大修真天才们借去力量.你们的每一个点赞,每一个关注都是让我回到九州世界的助力,兄 ...