2009-12-20 45 views
5

我们正在使用HttpURLConnection的API调用REST API到同一服务提供商通常(一种聚合用例的)。我们希望始终向提供者主机开放5个连接池(始终是相同的IP)。如何保持多个Java HttpConnections开到相同的目的地

什么是合适的解决方案?下面是我们的尝试:


System.setProperty("http.maxConnections", 5); // set globally only once 
... 
// everytime we need a connection, we use the following 
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection(); 
conn.setRequestMethod("GET"); 
conn.setDoInput(true); 
conn.setDoOutput(false); 
conn.setUseCaches(true); 
... 
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
... 

在这一点上,我们读到输入流,直到BufferedReader中返回没有更多的字节。如果我们想要重新使用提供者的底层连接,我们在那之后会做什么?我们的印象是,如果输入流被完全读取,则连接将被添加回池中。

它已经这样工作了好几个星期,但今天它停止工作产生此异常:java.net.SocketException: Too many open files

我们发现在这样CLOSE_WAIT状态无数插槽(通过运行lsof): java 1814 root 97u IPv6 844702 TCP colinux:58517->123.123.254.205:www (CLOSE_WAIT)

韩元't要么conn.getInputStream()。close()或conn.disconnect()完全关闭连接并将其从池中删除?

回答

4

here

当前实现不缓冲响应主体。这意味着应用程序必须完成读取响应主体或调用close()来放弃响应主体的其余部分,以便重用该连接。而且,当前的实现在清理连接时不会尝试读取块,这意味着如果整个响应主体不可用,连接将不会被重用。

我读到这个就好像您的解决方案应该可以工作,但您也可以自由调用close并且连接仍然会被重用。

4

我们还对Java 5的这个问题,我们的解决方案是切换到Apache的HttpClient与连接池管理器。

用于HTTP的Sun的URL处理程序的keepalive实现非常麻烦。没有维护线程来关闭空闲连接。

Keepalive的另一个更大的问题是您需要删除响应。否则,连接也将成为孤儿。大多数人不会正确处理错误流。请参阅我的回答这个问题,就如何正确读取错误响应的示例,

HttpURLConnection.getResponseCode() returns -1 on second invocation

+0

谢谢你,你是对的,我们不是一个错误过程中读取错误流。怎么样'stream.close()'和'conn.disconnect()'?他们可以使用还是会打败http保持活着的目的?我在某处写了代码,应该完全读取流,但不能关闭它,但我找不到参考atm。 – Lightbeard 2009-12-21 06:42:47

+0

如果要重新使用连接,则不应关闭流,也不要断开连接。正如我所说的,即使你做的都是正确的,至少在Java 5上仍然会出现套接字泄漏。 – 2009-12-21 14:00:30

2

reference通过不认引用是真正有帮助。

我们知道Apache HttpClient更好,但这需要另一个jar,我们可能会在applet中使用此代码。

调用HttpURLConnection.connect()是不必要的。我不确定它是否会阻止连接重用,但是我们将其取出。关闭流是安全的,但在连接上调用disconnect()将阻止重用。另外,设置sun.net.http.errorstream.enableBuffering=true有帮助。

以下是我们最后使用:


System.setProperty("http.maxConnections", String.valueOf(CONST.CONNECTION_LIMIT)); 
System.setProperty("sun.net.http.errorstream.enableBuffering", "true"); 

... 

int responseCode = -1; 
HttpURLConnection conn = null; 
BufferedReader reader = null; 
try { 
conn = (HttpURLConnection) (new URL(url)).openConnection(); 
conn.setRequestProperty("Accept-Encoding", "gzip"); 

// this blocks until the connection responds 
InputStream in = new GZIPInputStream(conn.getInputStream()); 

reader = new BufferedReader(new InputStreamReader(in)); 
StringBuffer sb = new StringBuffer(); 
char[] buff = new char[CONST.HTTP_BUFFER_SIZE]; 
int cnt; 

while((cnt = reader.read(buff)) > 0) sb.append(buff, 0, cnt); 

reader.close(); 

responseCode = conn.getResponseCode(); 
if(responseCode != HttpURLConnection.HTTP_OK) throw new IOException("abnormal HTTP response code:"+responseCode); 

return sb.toString(); 

} catch(IOException e) { 
    // consume error stream, otherwise, connection won't be reused 
    if(conn != null) { 
    try { 
     InputStream in = ((HttpURLConnection)conn).getErrorStream(); 
     in.close(); 
     if(reader != null) reader.close(); 
    } catch(IOException ex) { 
     log.fine(ex); 
    } 
    } 

    // log exception  
    String rc = (responseCode == -1) ? "unknown" : ""+responseCode; 
    log.severe("Error for HttpUtil.httpGet("+url+")\nServer returned an HTTP response code of '"+rc+"'"); 
    log.severe(e); 
} 
相关问题