5

我有 1000 个专用 Java 线程,每个线程每秒轮询一个相应的 url。

public class Poller { 
    public static Node poll(Node node) { 
        GetMethod method =  null; 
        try { 
            HttpClient client = new HttpClient(new SimpleHttpConnectionManager(true)); 
            ......
        } catch (IOException ex) { 
            ex.printStackTrace(); 
        } finally { 
            method.releaseConnection(); 
        } 
    } 
} 

线程每秒运行一次:

for (int i=0; i <1000; i++) { 
    MyThread thread = threads.get(i) // threads  is a static field 
    if(thread.isAlive()) { 
        // If the previous thread is still running, let it run. 
    } else { 
        thread.start(); 
    } 
}

问题是,如果我每隔一秒运行一次作业,我就会得到如下随机异常:

java.net.BindException: Address already in use 
 INFO httpclient.HttpMethodDirector: I/O exception (java.net.BindException) caught when processing request: Address already in use 
 INFO httpclient.HttpMethodDirector: Retrying request 

但如果我每 2 秒或更长时间运行一次作业,一切都会运行良好。

我什至尝试使用 shutdown() 关闭 SimpleHttpConnectionManager() 的实例,但没有任何效果。

如果我执行 netstat,我会看到数以千计的 TCP 连接处于 TIME_WAIT 状态,这意味着它们已关闭并正在清理。

因此,为了限制连接数,我尝试使用单个 HttpClient 实例并像这样使用它:

  public class MyHttpClientFactory { 
        private static MyHttpClientFactory instance = new HttpClientFactory(); 
        private MultiThreadedHttpConnectionManager connectionManager; 
        private HttpClient client; 

        private HttpClientFactory() { 
                init(); 
        } 

        public static HttpClientFactory getInstance() { 
                return instance; 
        } 

        public void init() { 
                connectionManager = new MultiThreadedHttpConnectionManager(); 
                HttpConnectionManagerParams managerParams = new HttpConnectionManagerParams(); 
                managerParams.setMaxTotalConnections(1000); 
                connectionManager.setParams(managerParams); 
                client = new HttpClient(connectionManager); 
        } 

        public HttpClient getHttpClient() { 
                if (client != null) { 
                        return client; 
                } else { 
                    init(); 
                    return client; 
                } 
        } 
}

然而,运行 2 小时后,它开始抛出“打开的文件太多”,最终什么也做不了。

ERROR java.net.SocketException: Too many open files
INFO httpclient.HttpMethodDirector: I/O exception (java.net.SocketException) caught when processing request: Too many open files
INFO httpclient.HttpMethodDirector: Retrying request

我应该能够增加允许的连接数并使其工作,但我只会延长邪恶。知道在上述情况下使用 HttpClient 的最佳做法是什么吗?

顺便说一句,我还在 HttpClient3.1 上。

4

3 回答 3

3

这发生在我们几个月前。首先,仔细检查以确保您确实每次都在调用 releaseConnection()。但即便如此,操作系统实际上并没有一次性回收所有 TCP 连接。解决方案是使用 Apache HTTP 客户端的MultiThreadedHttpConnectionManager。这会汇集并重用连接。

有关更多性能提示,请参阅http://hc.apache.org/httpclient-3.x/performance.html

更新:哎呀,我没有阅读较低的代码示例。如果您正在执行 releaseConnection() 并使用 MultiThreadedHttpConnectionManager,请考虑您的操作系统对每个进程打开文件的限制是否设置得足够高。我们也遇到了这个问题,需要稍微扩大限制。

于 2010-05-26T15:28:17.473 回答
2

第一个错误没有错。您刚刚耗尽了可用的经验端口。每个 TCP 连接可以保持 TIME_WAIT 状态 2 分钟。您生成 2000/秒。迟早,套接字找不到任何未使用的本地端口,您将收到该错误。TIME_WAIT 正是为此目的而设计的。没有它,您的系统可能会劫持以前的连接。

第二个错误意味着您打开了太多套接字。在某些系统上,打开文件的限制为 1K。由于挥之不去的套接字和其他打开的文件,您可能刚刚达到了该限制。在 Linux 上,您可以使用以下方法更改此限制

  ulimit -n 2048

但这受到系统范围最大值的限制。

于 2010-05-26T15:25:52.637 回答
0

作为 sudo 或 root 编辑 /etc/security/limits.conf 文件。在“# End of File”上方的文件末尾输入以下值: * soft nofile 65535 * hard nofile 65535 这会将打开文件的数量设置为无限制。

于 2012-08-20T08:48:42.853 回答