Tomcat,HTTP Keep-Alive和Java的HttpsUrlConnection

19
我有两个Tomcat服务器需要维持持久连接,以减少SSL握手次数。一个服务器(代理)位于DMZ中,而另一个服务器则安全地位于另一个防火墙后面。代理基本上只运行一个简单的servlet,在将请求转发到安全机器之前进行一些检查。在初始请求时,机器会交换证书,然后执行真正的工作。因此,我希望保持一个超时为几分钟的持久连接。
为了与安全服务器通信,代理上的servlet使用HttpsUrlConnection。我已经设置了WireShark,并注意到无论我为安全机器上的连接器设置什么样的keepAliveTimeout值,TCP连接都会在大约5或10秒后关闭。这个数字似乎与我读到的默认超时时间以及Java处理HTTP Keep-Alive的方式相匹配。这个链接解释了如果服务器发送了Keep-Alive超时,则Java会遵守该超时设置,否则在关闭连接之前,Java会使用5秒(直接连接)或10秒(代理连接)。
我想知道的是如何让Tomcat强制发送Keep-Alive头。不是Connection: Keep-Alive,而是Keep-Alive: timeout=x
我已经尝试过Apache HTTP服务器,并修改了httpd.conf中的keepAliveTimeout会导致Keep-Alive头更改其超时值。此外,Java确实遵守该超时时间。

更新(12/23/11):在进行了几次实验后,我尝试使用Apache的HttpClient(3.1)编写了一些简单粗暴的代码,而不是使用HttpsUrlConnection。当设置为使用Keep-Alive时,HttpClient似乎只是等待服务器关闭连接。但我不知道它会等多久。我希望保持HTTP连接活跃3到5分钟。


TCP连接在大约5或10秒后关闭。是哪一端?客户端还是服务器端? - user207421
它被客户端关闭。 - James Sheppard
1
您可以使用Apache Tomcat Connector在防火墙后面的服务器和DMZ服务器之间保持持久连接。安全机器可以配置为代理中的工作程序。请查看以下链接以获取更多信息:http://tomcat.apache.org/connectors-doc/generic_howto/workers.html - Rajesh Pantula
如果您正在使用ajp,请阅读有关工作程序中连接超时的内容,因为如上所述。我们曾经遇到过类似的问题,而ajp的工作程序修复了超时问题。希望这可以帮助您。 - Sergey Benner
1
@James,你应该自己发布一个答案并接受它。这是完全可以的。这也有助于保持网站的整洁 :) - Kevin D
显示剩余2条评论
4个回答

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

虽然它可以减少SSL握手次数,但请记住,这种方法(保持连接活动)很容易导致Tomcat服务器耗尽工作线程的情况。因此,请确保客户端(在您的情况下为代理服务器)在不再需要使用连接时关闭连接。 - elirandav

6

在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本身决定关闭,那么客户端将失败。 - kisna
timeout=60000 - 这意味着60000秒。 - Viktor Mukhachev

2

Java API中的Http(s)UrlConnection可以无缝支持Keep-Alive信息,并根据以下详细说明为每个服务器主机管理连接池(请仔细阅读-每个细节都很重要)。

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

当然,HttpClient的约束较少,但处理您的情况的最佳方法是使用其MultiThreadedHttpConnectionManager,感谢以下指南


0
如果您可以将第一个(外部)服务器升级到Java 19或更高版本,则可以使用此Java系统属性,在服务器未指定时设置客户端保持活动时间:
http.keepAlive.time.server=60

默认值为5秒。上述设置为1分钟。在连接的客户端端更改为较高的值将解决您的问题(注意:仅适用于Java 19及更高版本)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接