您好我正在尝试编写一个向多个服务器(已经支持 http2)发出 http2 请求的客户端。到目前为止,我已经编写了这个运行良好的独立程序,我可以看到所有请求都通过相同的 tcp 连接(在 wireshark 的帮助下验证)。我使用带有 conscrypt 的 Apache Httpclient 5 来支持 jdk8 中的 ALPN(jdk8 是我的要求,我无法升级到 jdk9 或更高版本)
主要块就像,
try {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager(){
public X509Certificate[] getAcceptedIssuers(){ return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
Provider provider = Conscrypt.newProvider();
SSLContext sslContext = SSLContext.getInstance("TLSv1.3", provider);
sslContext.init(null, trustAllCerts, new SecureRandom());
client = Http2AsyncClientBuilder.create().setTlsStrategy(new ConscryptClientTlsStrategy(sslContext)).setH2Config(H2Config.DEFAULT).build();
client.start();
ThreadPoolExecutor tpe = (ThreadPoolExecutor)Executors.newFixedThreadPool(50);
for (int i=0; i < 50; i++) {
Runnable worker = new WorkerClass(client, i);
tpe.execute(worker);
}
} catch (Exception e) {
e.printStackTrace();
}
可运行的就像,
static class WorkerClass implements Runnable {
CloseableHttpAsyncClient client = null;
int i = 0;
WorkerClass(CloseableHttpAsyncClient client, int i) {
this.client = client;
this.i = i;
}
public void run() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.of(15, TimeUnit.SECONDS))//Determines the timeout until a new connection is fully established.
.setConnectionRequestTimeout(Timeout.of(60, TimeUnit.SECONDS))//Returns the connection lease request timeout used when requesting a connection from the connection manager.
.setResponseTimeout(Timeout.of(60, TimeUnit.SECONDS))//Determines the timeout until arrival of a response from the opposite endpoint.
.build();
String url = "https://localhost:8081/myagent/getOutput?"+System.currentTimeMillis();
String cachedXMLRequest = "<?xml version=\"1.0\" standalone=\"no\"?><My XML REQUEST GOES HERE>";
SimpleHttpRequest request= SimpleHttpRequests.POST.create(url);
request.setBodyText(cachedXMLRequest, ContentType.APPLICATION_JSON);
request.setConfig(requestConfig);
final CountDownLatch latch = new CountDownLatch(1);
client.execute(request, new FutureCallback<SimpleHttpResponse>() {
@Override
public void cancelled() {
System.out.println("Cancelled");
latch.countDown();
}
@Override
public void completed(SimpleHttpResponse arg0) {
System.out.println("Completed "+arg0.getBodyText());
latch.countDown();
}
@Override
public void failed(Exception exception) {
System.out.println("Failed ");
exception.printStackTrace();
latch.countDown();
}
});
try {
latch.await(60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
所以我在我的客户端中模拟了这个独立的,它使用上述逻辑异步调用多个服务器进行轮询。但是在这里我可以看到每个请求有一个 tcp 连接,并且连接在请求结束时关闭(尽管响应标头返回为 HTTP/2.0)。我使用主块代码在我的应用程序启动时初始化客户端(除了线程池部分,因为它用于独立模拟多线程环境)并创建 url、请求和响应对象并在线程模型中同时执行每个请求(异步多路复用)。我的要求是每个域使用一个 tcp 连接并长时间使用它来处理大量请求。我在这里想念什么?
或者有人用正确的方式帮助我使用 httpclient 5 处理 http2
Kindy对此有所了解。
编辑:
我追踪到两件事是异常连接关闭,
1. 每次请求后关闭连接
原因:这条线造成的
RequestConfig requestConfig = RequestConfig.custom()。setConnectTimeout(Timeout.of(15, TimeUnit.SECONDS)) ...
此 setConnectTimeout 应该处于请求级别,但在 15 秒空闲后它会关闭 tcp 连接本身,我不知道这是错误还是预期行为,因为此配置用于根据文档封装请求项。
删除此行允许 tcp 连接在请求之外保持活动状态。
2. 由于非法 HEADERS 帧/协议错误/内部错误
我可以在线路日志中看到这些异常,正是我的 tcp 连接关闭的位置(它随机关闭),我无法共享整个日志,因为有太多我只是发布了异常部分
0x0][0x1][0x0][0x0][0x4][0xffffffc9]"
2019-02-07 16:05:08,666 调试 - i/o-0000001B << 流 0 帧:WINDOW_UPDATE (0x8);标志:(0x0);长度:4
2019-02-07 16:05:08,666 调试 - i/o-0000001B << 增量 1225
2019-02-07 16:05:08,666 调试 - i/o-0000001B >> 流 0 流控制 1225 -> 65535
2019-02-07 16:05:08,666 调试 - i/o-0000001B << 流 1 帧:WINDOW_UPDATE (0x8);标志:(0x0);长度:4
2019-02-07 16:05:08,666 调试 - i/o-0000001B << 增量 1225
2019-02-07 16:05:08,666 调试 - i/o-0000001B >> 流 1 流控制 1225 -> 65535
2019-02-07 16:05:08,827 调试 - i/o-0000001B << "[0x0][0x0][0x1d][0x1][0x4][0x0][0x0][0x0][0x5]?[0xffffffe1 ]?[0xffffff88][0x1f][0x12][0xffffff96][0xffffffdf]=[0xffffffbf]J[0x1][0xffffffd5]0[0xffffff96]5[0x4][0x0][0xffffffbe][0xffffffa0]Aq[0xffffff91][ 0x10][0xffffff94][0xffffffc5][0xffffffa3][0x0][0x3]E[0x0][0x1][0x0][0x0][0x0][0x5][\n]"
2019-02-07 16:05:08,827 调试-i/o-0000001B <<“[\n]”
2019-02-07 16:05:08,827 调试 - i/o-0000001B << ""
2019-02-07 16:05:08,827 调试 - i/o-0000001B << 流 5 帧:标题(0x1);标志:END_HEADERS (0x4);长度:29
2019-02-07 16:05:08,828 调试 - i/o-0000001B << ?.?. ...=.J..0.5 3f e1 3f 88 1f 12 96 df 3d bf 4a 01 d5 30 96 35
2019-02-07 16:05:08,828 调试 - i/o-0000001B << ....Aq..... 04 00 是 a0 41 71 91 5c 10 94 c5 a3 7f
2019-02-07 16:05:08,828 调试 - i/o-0000001B >> 流 0 帧:GOAWAY (0x7);标志:(0x0);长度:29
2019-02-07 16:05:08,828 调试 - i/o-0000001B >> 最后一个流 0
2019-02-07 16:05:08,829 调试 - i/o-0000001B >> 代码 PROTOCOL_ERROR
2019-02-07 16:05:08,829 调试 - i/o-0000001B >> 非法 HEADERS 框架
2019-02-07 16:05:08,829 调试 - i/o-0000001B >> "[0x0][0x0][0x1d][0x7][0x0][0x0][0x0][0x0][0x0][0x0] [0x0][0x0][0x0][0x0][0x0][0x0][0x1]非法头帧”
2019-02-07 16:05:10,401 DEBUG - ex-0000018D:准备请求执行
和,
2019-02-07 16:17:17,519 调试 - i/o-0000000C << 流 0 帧:WINDOW_UPDATE (0x8);标志:(0x0);长度:4
2019-02-07 16:17:17,519 调试 - i/o-0000000C << 增量 1208
2019-02-07 16:17:17,519 调试 - i/o-0000000C >> 流 0 流控制 1208 -> 65535
2019-02-07 16:17:17,519 调试 - i/o-0000000C << 流 1 帧:WINDOW_UPDATE (0x8); 标志:(0x0);长度:4
2019-02-07 16:17:17,519 调试 - i/o-0000000C << 增量 1208
2019-02-07 16:17:17,519 调试 - i/o-0000000C >> 流 1 流控制 1208 -> 65535
2019-02-07 16:17:17,569 调试 - i/o-0000000C << "[0x0][0x0][0x4][0x3][0x0][0x0][0x0][0x0][0x3][0x0] [0x0][0x0][0x2]"
2019-02-07 16:17:17,569 调试 - i/o-0000000C << 流 3 帧:RST_STREAM (0x3); 标志:(0x0);长度:4
2019-02-07 16:17:17,569 调试 - i/o-0000000C << 代码 INTERNAL_ERROR
2019-02-07 16:17:17,569 调试 - i/o-0000000C >> 流 0 帧:GOAWAY (0x7); 标志:(0x0);长度:31
2019-02-07 16:17:17,569 调试 - i/o-0000000C >> 最后一个流 0
2019-02-07 16:17:17,569 调试 - i/o-0000000C >> 代码 PROTOCOL_ERROR
2019-02-07 16:17:17,569 调试 - i/o-0000000C >> 意外的流 ID:3
我不知道是什么导致了这些异常,我尝试使用静态和动态帖子正文数据,并且两者的行为相似
完整会话日志:
https://drive.google.com/open?id=12y8HnaMTrPo-NBeLaoCYpK6zklJBL56T
https://drive.google.com/open?id=16KHgqLWrwz3Z3ls3Yvpp58zOI3SUNATM
客户端:Tomcat 9.0x,Jdk 1.8.x(带有用于 ALPN 支持的 conscrypt,我们通过 TLSv1.3 连接它)在 ubuntu 16.x 上
服务器:Tomcat 9.0x(带有 openssl 实现、NIO 连接器、http2 支持)、用于 TLSv1.3 支持的 Openssl 1.1.1、Ubuntu 14.x 上的 JDK 10.0.2
任何帮助将不胜感激
TIA