恐怕在Java 11的http客户端API中似乎不可能实现。
最相关的JDK内部类是Http2Connection.java。 它列出了我们期望的HTTP / 2连接的三种“创建”情况:
- 升级的HTTP / 1.1普通tcp连接
- 直接创建的明文tcp连接(使用先前知识)
- 直接创建的使用ALPN的HTTP / 2 SSL连接。
我们感兴趣的是第2种情况。 这里有相应的代码:
private Http2Connection(HttpRequestImpl request,
Http2ClientImpl h2client,
HttpConnection connection)
throws IOException
{
...
}
唉,这个构造函数实际上只从安全连接的方法中使用:
static CompletableFuture<Http2Connection> createAsync(HttpRequestImpl request,
Http2ClientImpl h2client,
Exchange<?> exchange) {
assert request.secure();
AbstractAsyncSSLConnection connection = (AbstractAsyncSSLConnection)
...
return connection.connectAsync(exchange)
.thenCompose(unused -> connection.finishConnect())
.thenCompose(unused -> checkSSLConfig(connection))
.thenCompose(notused-> {
CompletableFuture<Http2Connection> cf = new MinimalFuture<>();
try {
Http2Connection hc = new Http2Connection(request, h2client, connection);
cf.complete(hc);
} catch (IOException e) {
cf.completeExceptionally(e);
}
return cf; } );
非 TLS 构造函数似乎仅在升级 HTTP 1.1 连接时才会被调用。
我也研究了许多其他类,但我看不到任何迹象表明JDK 11客户端可以通过 HTTP 1.1 升级以外的方法协商明文 HTTP/2 连接。
我的发现与
HttpClient.Builder 的 API 描述非常相似,它只是关于选择 HTTP/2 版本的说明如下:
如果设置为 HTTP/2,则每个请求都将尝试升级到 HTTP/2。如果升级成功,则对此请求的响应将使用 HTTP/2,并且所有后续请求和相应都将使用 HTTP/2。如果升级失败,则响应将使用 HTTP/1.1 处理。
HTTP/2连接复用中有很多有趣的事情。例如,在并发请求升级时,只有一个请求被保留在HTTP/2连接“缓存”中以备将来使用。当流的数量达到最大值(约为2^31-1)时,缓存的连接将过期,以便进一步的请求不会超出该限制,但现有的流将保持打开状态。这是很多流。如果你的用例和我一样——在非常长时间运行的应用程序中作为Web服务客户端——那么升级的成本实际上仅针对第一个请求是微不足道的。我仍然想消除它,主要是为了简单起见,但我开始觉得这并不值得。
像提问者一样,我想坚持使用JDK客户端。我只想提一下,Apache HttpClient 5 beta似乎支持强制使用HTTP/2(HttpVersionPolicy.FORCE_HTTP_2)。我无法进一步提供建议,因为beta版本没有太多文档,注释也比JDK实现稀少得多。但如果我尝试并成功了,我会更新我的答案。