我的应用程序将会每分钟发送大约1000个POST请求到我的tomcat服务器(该服务器启用了http/2),该服务器将轮询给定的URL并返回HTML和响应时间,我希望实现真正的http/2多路复用以重用应用程序和tomcat服务器之间的TCP连接。我的客户端使用OkHttp,我可以成功建立连接并长时间重用它,但当请求计数增加时(例如,排队:50,运行:50),响应时间也会增加(它会飙升到2000毫秒-15000毫秒甚至更高,而通常只需要300毫秒-500毫秒)。我可以理解这是由于过多请求导致的TCP连接过载,所以我决定打开多个TCP连接并允许其在TCP连接之间分配请求负载。我强制客户端使用
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(100);
dispatcher.setMaxRequestsPerHost(5);
ConnectionPool cp = new ConnectionPool(5, 5, TimeUnit.MINUTES);
通过使用Wireshark,我可以看到已经打开了5个连接,并且可以看到其中4个请求在第一次握手成功后立即关闭,我不知道这是否是http/2多路复用的预期行为。
如果这是预期的行为,那么如何进行优化以减少峰值负载时间中每个请求的响应时间?
或者在峰值负载时间使用多个连接来分配负载是否可行?
以下是我的示例程序:
Security.insertProviderAt(Conscrypt.newProvider(), 1);
sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
}, new java.security.SecureRandom());
sslSocketFactory = sslContext.getSocketFactory();
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(100);
dispatcher.setMaxRequestsPerHost(5);
ConnectionPool cp = new ConnectionPool(5, 1, TimeUnit.DAYS);
okHttpClient = new OkHttpClient().newBuilder()
.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustManager[0])
.dispatcher(dispatcher)
.connectionPool(cp)
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})
.build();
我们使用 conscryt 来在 jdk8 中启用 ALPN。
try {
String url = "https://localhost:8081/myAent/getOutput?1000000004286_1520339351002_"+System.currentTimeMillis();
String json = "<?xml version=\"1.0\" standalone=\"no\"?><UC mid=\"1000000005011\" pollNow=\"true\" use_ipv6=\"false\" rca=\"0\" rcaFN=\"P|TA|D|J|M\" pFtct=\"\" sFtct=\"\" issecondarydc=\"false\" ssDc=\"false\" nocache=\"1\" storeHtmlResp=\"true\" storeTroubleScrnSht=\"false\" moConfig=\"false\" xconlen=\"8265\" noScreenshotRecheckForSSLErrors=\"true\" avgDnsTime=\"null\" isProxyRequired=\"false\" userroles=\"EVAL_USER\" uid=\"102030230293029021\" retryDelay=\"2\" retry=\"true\" idcLocUrl=\"http://localhost:8080/app/receivemultipartdata\" isRemoteAgent=\"true\" sendHeaders=\"false\" api=\"ab_345nnn4l4lj4lk23nl4k23342lk4l23j4\" ut=\""+(System.currentTimeMillis()+"")+"\" mt=\"URL\" dctimeout=\"70\" pollinterval=\"1440\" locid=\"48\" log=\"1\" currentstatus=\"1\" postUrl=\"https://example.com\"><Url acc=\"\" forced_ips=\"\" use_ipv6=\"false\" client_cert=\"\" mid=\"1000000005011\" sotimeout=\"60\" ua=\"\" ds=\"117.20.43.94\" ucc=\"\" md=\"false\" client_cert_pass=\"\" context=\"default\" unavail_alert=\"\" ssl_protocol=\"\" avail_alert=\"\" enabledns=\"false\" enableBouncyCastle=\"false\" cc=\"false\" a=\"https://www.example.com/tools.html\" upStatusCodes=\"\" regex_alert=\"\" probeproxy=\"false\" m=\"G\" keyword_case=\"0\" regex=\"\" rbc=\"\" t=\"30\" lc=\"English\"><PD></PD><CH hn=\"\" hd=\"_sep_\" hv=\"\"/><AI ps=\"\" un=\"\"/></Url></UC>";
RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
long nanoStartTime = System.nanoTime();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("okhttp3:: Request failed"+ e);
}
@Override
public void onResponse(Call call, okhttp3.Response responseObj) throws IOException {
try (ResponseBody body = responseObj.body()) {
long nanoEndTime = System.nanoTime();
long nanoDiffTime = TimeUnit.NANOSECONDS.toMillis(nanoEndTime - nanoStartTime);
System.out.println("okhttp3:: Succeded response ***"+body+"$$$");
System.out.println("okhttp3:: Request Succeded protocol ***"+responseObj.protocol()+"$$$, time is "+nanoDiffTime);
}
}
});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
如何优化OkHttpClient以使用多个TCP套接字/连接实现HTTP/2多路复用,以分配请求负载?
客户端: Tomcat Apache - 9.0.x, JDK - 8, HTTP库 - Okhttp3, 操作系统 - Ubuntu / Centos, 安全提供程序 - Conscrypt(用于支持JDK 8中的ALPN)。
服务器: Tomcat Apache - 9.0.16, JDK - 10.0.1, 操作系统 - Ubuntu / Centos, OpenSSL - 1.1.1a用于支持TLSv1.3