Tomcat、HTTP Keep-Alive 和 Java 的 HttpsUrlConnection

Posted

技术标签:

【中文标题】Tomcat、HTTP Keep-Alive 和 Java 的 HttpsUrlConnection【英文标题】:Tomcat, HTTP Keep-Alive and Java's HttpsUrlConnection 【发布时间】:2012-01-26 07:41:18 【问题描述】:

我有两台 Tomcat 服务器,它们需要保持持久连接以减少 SSL 握手。一台服务器(代理)位于 DMZ 中,而另一台则安全地位于另一个防火墙后面。代理基本上只是运行一个简单的 servlet,在将请求转发到安全机器之前进行一些完整性检查。根据最初的请求,机器在执行实际工作之前会交换证书。因此,我想保持一个持续连接,超时几分钟。

为了与安全服务器通信,代理上的 servlet 使用HttpsUrlConnection。我已经设置了 WireShark,我注意到无论我为安全机器上的连接器设置什么 keepAliveTimeout 值,TCP 连接都会在大约 5 或 10 秒后关闭。这个数字似乎与我读到的默认超时以及 Java 如何处理 HTTP Keep-Alive 相匹配。这个link 解释说,如果Java 是由服务器发送的,那么它会遵守Keep-Alive 超时,否则它会在关闭连接之前使用5 秒(直接连接)或10 秒(代理连接)。

我想弄清楚的是如何强制 Tomcat 发送 Keep-Alive 标头。不是Connection: Keep-Alive,而是Keep-Alive: timeout=x

我已经尝试过使用 Apache HTTP 服务器并修改 httpd.conf 中的 keepAliveTimeout 确实会导致 Keep-Alive 标头更改其超时值。此外,Java 确实遵守此超时。

更新(2011 年 12 月 23 日):在运行了一些实验之后,我尝试使用 Apache 的 HttpClient (3.1) 而不是 HttpsUrlConnection 编写一些快速而肮脏的代码。看起来 HttpClient 设置为使用 Keep-Alive 时,只是等待服务器关闭连接。虽然不知道要等多久。我打算让 HTTP 连接保持活动 3 到 5 分钟。

【问题讨论】:

“大约 5 或 10 秒后 TCP 连接关闭”。到哪一端?客户端还是服务器? 被客户端关闭。 你知道,你描述的场景; DMZ 中的 WebServer 将请求转发到某个防火墙后面的 AppServers,通常由 DMZ 中的 Apache HTTPD 处理,执行 https 操作并使用 AJP 连接器 (tomcat.apache.org/tomcat-4.0-doc/config/ajp.html) 将请求转发到 Tomcat AppServers。这是避免 SSL 开销的另一种方法。 您可以使用 apache tomcat 连接器在防火墙后面的服务器和 DMZ 服务器之间保持持久连接,可以将安全机器配置为代理中的工作人员。检查此以获取更多信息tomcat.apache.org/connectors-doc/generic_howto/workers.html @James 你应该自己发布一个答案并接受它。这是一件非常好的事情。它还有助于保持网站整洁:) 【参考方案1】:

通过将 Tomcat 连接器中的 keepAliveTimeout 设置为 300000,我能够使用 HttpClient 3.1 将 HTTP 连接保持打开 5 分钟。我使用 WireShark 验证了服务器将终止连接,而 HttpClient 只会等待。通过 HttpClient 的后续请求会重用现有的 TCP 连接(避免任何进一步的 SSL 握手)。但关键是要有一个 HttpClient 实例(即不是每次都创建一个)。这对大多数人来说可能很明显,但我不确定 HTTPClient 的 API 机制是什么。简而言之,创建一个 HttpClient 实例,并为每个请求(POST、GET 等)创建一个新的 PostMethod、GetMethod 等。这将导致 TCP 连接被重用。

【讨论】:

虽然它减少了 SSL 握手的次数,但请记住,这种方法(保持连接处于活动状态)很容易导致服务器中的 tomcat 用完工作线程的情况。因此,只要确保客户端(在您的情况下为代理服务器)不再希望使用它就关闭连接。【参考方案2】:

在 Tomcat 中,我设法使用 HttpServletResponse.addHeader() 在我的 HttpServelet 中设置标题,如下所示:

  response.addHeader("Connection", "Keep-Alive");
  response.addHeader("Keep-Alive", "timeout=60000");

我向 WireShark 确认这确实适用于客户端的 HttpUrlConnection。不设置“Connection”头是不行的,所以两个都需要设置。

【讨论】:

如果添加了 Keep-Alive 配置,连接头应该由 tomcat 自动设置。您正在从 tomcat 的 server.xml 复制 Keep-Alive 以及返回自定义 Keep-Alive 标头的代码。如果应用程序超时时间长于 tomcat 或 tomcat 本身决定关闭,那么客户端将失败。 timeout=60000 - 这意味着 60000 秒【参考方案3】:

Java API Http(s)UrlConnection 无缝支持Keep-Alive 信息,并根据following detailed explanation 管理每个服务器主机的连接池(仔细阅读 - 每个细节都很重要)。

在这种情况下,您的代码必须完全读取缓冲区、关闭流并在出现IOException 的情况下读取错误。

当然 HttpClient 的限制较少,但处理您的情况的最佳方法是使用它的 MultiThreadedHttpConnectionManager,感谢 following guidelines。

【讨论】:

以上是关于Tomcat、HTTP Keep-Alive 和 Java 的 HttpsUrlConnection的主要内容,如果未能解决你的问题,请参考以下文章

HTTP协议Keep-Alive模式详解和HTTP头字段总结

HTTP协议Keep-Alive模式详解和HTTP头字段总结

Http长连接和Keep-Alive以及Tcp的Keepalive

谈谈Http长连接和Keep-Alive以及Tcp的Keepalive

HTTP Keep-Alive模式

HTTP keep-alive和TCP keepalive的区别,你了解吗?