简体   繁体   English

BindException:地址已在客户端套接字上使用吗?

[英]BindException: address already in use on a client socket?

I've got a client-server tiered architecture with the client making RPC-like requests to the server. 我有一个客户端-服务器分层的体系结构,客户端向服务器发出类似RPC的请求。 I'm using Tomcat to host the servlets, and the Apache HttpClient to make requests to it. 我正在使用Tomcat托管servlet,并使用Apache HttpClient对其进行请求。

My code goes something like this: 我的代码是这样的:

    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;

It works great in a lightly-loaded environment, but when I'm making hundreds of requests per second I start to see this - 它在负载较轻的环境中效果很好,但是当我每秒发出数百个请求时,我开始看到这种情况-

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)

Any thoughts on how to fix this? 有关如何解决此问题的任何想法? I'm guessing it's something to do with the client trying to reuse the ephemeral client ports, but why is this happening / how can I fix it? 我猜想这与客户端尝试重用临时客户端端口有关,但是为什么会这样/我该如何解决? Thanks! 谢谢!

A very good discussion of the problem you are running into can be found here . 这里可以找到有关您遇到的问题的很好的讨论。 On the Tomcat side, by default it will use the SO_REUSEADDR option, which will allow the server to reuse sockets which are in TIME_WAIT. 在Tomcat端,默认情况下它将使用SO_REUSEADDR选项,该选项将允许服务器重用TIME_WAIT中的套接字。 Additionally, the Apache http client will by default use keep-alives, and attempt to reuse connections. 此外,默认情况下,Apache http客户端将使用保持活动状态,并尝试重用连接。

Your problems seems to be caused by not calling releaseConnection on the HttpClient. 您的问题似乎是由于未在HttpClient上调用releaseConnection引起的。 This is required in order for the connection to be reused. 为了重新使用连接,这是必需的。 Otherwise, the connection will remain open until garbage collector comes and closes it, or the server disconnects the keep-alive. 否则,连接将保持打开状态,直到垃圾收集器出现并关闭它,或者服务器断开了保持活动状态。 In both cases, it won't be returned to the pool. 在这两种情况下,都不会将其退回到池中。

With hundreds of connections a second, and without knowing how long your connections keep to open, do their thing, close, and get recycled, I suspect that this is just a problem you're going to have. 每秒有数百个连接,并且不知道连接保持打开状态,进行操作,关闭和回收的时间长短,我怀疑这只是您要解决的问题。 One thing you can do is catch the BindException in your try block, use that to do anything you need to do in the bind-unsuccessful case, and wrap the whole call in a while loop that depends on a flag indicating whether the bind succeeded. 您可以做的一件事是在try块中捕获BindException ,在绑定失败的情况下使用它来做您需要做的任何事情,然后将整个调用包装在一个while循环中,该循环取决于指示绑定是否成功的标志。 Off the top of my head: 从我的头顶上:

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) {
        ...
    }
}

Update with question: One curious question: what are the maximum total and per-host number of connections allowed by your MultiThreadedHttpConnectionManager ? 问题更新:一个奇怪的问题: MultiThreadedHttpConnectionManager允许的最大总连接数和每主机连接数是多少? In your code, that'd be: 在您的代码中,将是:

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

Thus, you've fired more requests than TCP/IP ports are allowed to be opened. 因此,您发出的请求数量超过允许打开TCP / IP端口的数量。 I don't do HttpClient, so I can't go in detail about this, but in theory there are three solutions for this particular problem: 我不使用HttpClient,因此无法对此进行详细介绍,但是从理论上讲,针对此特定问题有三种解决方案:

  1. Hardware based: add another NIC (network interface card). 基于硬件:添加另一个NIC(网络接口卡)。
  2. Software based: close connections directly after use and/or increase the connection timeout. 基于软件:使用后直接关闭连接和/或增加连接超时。
  3. Platform based: increase the amount of TCP/IP ports which are allowed to be opened. 基于平台:增加允许打开的TCP / IP端口的数量。 May be OS-specific and/or NIC driver-specific. 可能是特定于OS和/或NIC驱动程序。 The absolute maximum is 65535, of which several may already be reserved/in use (eg port 80). 绝对最大值为65535,其中一些可能已经被保留/使用(例如,端口80)。

So it turns out the problem was that one of the other HttpClient instances accidentally wasn't using the MultiThreadedHttpConnectionManager I instantiated, so I effectively had no rate limiting at all. 因此事实证明,问题在于其他HttpClient实例之一意外地没有使用我实例化的MultiThreadedHttpConnectionManager,因此我实际上根本没有速率限制。 Fixing this problem fixed the exception being thrown. 解决此问题的方法解决了引发异常的问题。

Thanks for all the suggestions, though! 不过,感谢您的所有建议!

Even though we invoke HttpClientUtils.closeQuietly(client); 即使我们调用HttpClientUtils.closeQuietly(client); but in your code in case trying to read the content from HttpResponse entity like InputStream contentStream = HttpResponse.getEntity().getContent(), then you should close the inputstream also then only HttpClient connection get closed properly. 但是在您的代码中,如果试图从HttpResponse实体(例如InputStream contentStream = HttpResponse.getEntity()。getContent())读取内容,那么您还应该关闭inputstream,然后只有HttpClient连接正确关闭。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM