2009-12-30 56 views
2

我有一个客户端 - 服务器分层架构,客户端向服务器发出RPC类请求。我使用Tomcat托管servlet,并使用Apache HttpClient向它发出请求。BindException:地址已在客户端套接字上使用?

我的代码是这样的:

private static final HttpConnectionManager CONN_MGR = new MultiThreadedHttpConnectionManager(); 
    final GetMethod get = new GetMethod(); 
    final HttpClient httpClient = new HttpClient(CONN_MGR); 
    get.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES); 
    get.getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT); 

    get.setQueryString(encodedParams); 
    int responseCode; 
    try { 
     responseCode = httpClient.executeMethod(get); 
    } catch (final IOException e) { 
     ... 
    } 
    if (responseCode != 200) 
     throw new Exception(...); 

    String responseHTML; 
    try { 
     responseHTML = get.getResponseBodyAsString(100*1024*1024); 
    } catch (final IOException e) { 
     ... 
    } 
    return responseHTML; 

它在轻负载环境下的伟大工程,但是当我在做每秒数百个请求,我开始看到这一点 -

Caused by: java.net.BindException: Address already in use 
    at java.net.PlainSocketImpl.socketBind(Native Method) 
    at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:336) 
    at java.net.Socket.bind(Socket.java:588) 
    at java.net.Socket.<init>(Socket.java:387) 
    at java.net.Socket.<init>(Socket.java:263) 
    at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80) 
    at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122) 
    at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707) 
    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387) 
    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171) 
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397) 
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323) 

有关如何解决此问题的任何想法?我猜这是与客户端试图重用短暂客户端端口有关,但为什么会发生这种情况/我该如何解决它? 谢谢!

回答

0

所以事实证明,问题是其中一个HttpClient实例意外地没有使用MultiThreadedHttpConnectionManager实例化,所以我实际上没有任何速率限制。解决这个问题解决了抛出的异常。

感谢所有的建议,但!

1

每秒连接数百个连接,不知道你的连接持续打开多久,做完他们的事情,关闭并得到回收,我怀疑这只是你将要面临的一个问题。你可以做的一件事是在你的try块中捕获BindException,在绑定不成功的情况下使用它来做任何事情,并将整个调用包装在一个while循环中,该循环取决于指示绑定是否成功的标志。关闭我的头顶:

boolean hasBound = false; 
while (!hasBound) { 
    try { 
     hasBound = true; 
     responseCode = httpClient.executeMethod(get); 
    } catch (BindException e) { 
     // do anything you want in the bound-unsuccessful case 
    } catch (final IOException e) { 
     ... 
    } 
} 

更新与问题:一个奇怪的问题:什么是你的MultiThreadedHttpConnectionManager允许的最大连接总量和人均主机数?在你的代码中,这应该是:

CONN_MGR.getParams().getDefaultMaxConnectionsPerHost(); 
CONN_MGR.getParams().getMaxTotalConnections(); 
+0

每个主机:2,最大:20。在世界范围内,我能如何超过任何套接字限制与这种保守的默认值......:-p – 2009-12-30 02:42:34

0

因此,你已经发出了比TCP/IP端口允许打开更多的请求。我不做HttpClient,所以我不能详细介绍这个,但理论上有三个解决方案针对这个特定问题:

  1. 基于硬件:添加另一个NIC(网络接口卡)。
  2. 基于软件:使用后直接关闭连接和/或增加连接超时。
  3. 基于平台:增加允许打开的TCP/IP端口数量。可能是操作系统特定的和/或NIC驱动程序特定的。绝对最大值是65535,其中几个可能已经被保留/正在使用(例如端口80)。
+0

它会不会失败,更多的异常会导致超出上述资源?大部分资源耗尽错误来自socket()调用 - 联机帮助页面至少提到了三种不同的方式,可能由于缺乏资源而失败,而绑定手册页则没有。我期望Java异常来表明这一点。你是否仍然认为这可能是一个问题? – 2009-12-30 02:32:23

+0

只有当HttpClient被编码时才会出现这样的异常 - 我没有看过'HttpClient#executeMethod'的代码,但是根据你的经验,看起来他们没有。有理由认为他们可能在内部发现了BindException,并在不同的客户端端口上重新尝试了连接,但是再一次,这会打开一整桶蠕虫 - 它们会重试多少次?他们怎么知道这就是你想要做的? - 它也是有道理的,他们只是抛出BindException并让你决定。 – delfuego 2009-12-30 02:48:58

+0

通过查看跟踪,您可以看到它直接从本机Socket连接方法抛出 – 2009-12-30 08:00:14

1

关于您遇到的问题的一个很好的讨论可以找到here。在Tomcat方面,默认情况下它将使用SO_REUSEADDR选项,这将允许服务器重用处于TIME_WAIT中的套接字。另外,Apache http客户端默认使用保持链接,并尝试重新使用连接。

您的问题似乎是由于没有在HttpClient上调用releaseConnection造成的。这是连接重复使用所必需的。否则,连接将保持打开状态,直到垃圾收集器进入并关闭它,或者服务器断开保持活动状态。在这两种情况下,它都不会返回到池中。

0

即使我们调用HttpClientUtils.closeQuietly(client);但是在你的代码中,如果试图从InputStream的ContentStream = HttpResponse.getEntity()。getContent()读取HttpResponse实体的内容,那么你应该关闭输入流,然后只有HttpClient连接才能正常关闭。

相关问题