как сохранить несколько открытых соединений Http Java для одного и того же места назначения - PullRequest
5 голосов
/ 20 декабря 2009

Мы используем HttpURLConnection API, чтобы часто вызывать REST API для одного и того же провайдера (тип агрегации). Мы хотим, чтобы пул из 5 соединений всегда был открыт для хоста провайдера (всегда один и тот же IP-адрес).

Какое правильное решение? Вот что мы попробовали:


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)

Не будет ли conn.getInputStream (). Close () или conn.disconnect () полностью закрыть соединение и удалить его из пула?

Ответы [ 3 ]

4 голосов
/ 20 декабря 2009

У нас была эта проблема и на Java 5, и наше решение - перейти на Apache HttpClient с диспетчером соединений в пуле.

Служебная реализация обработчика URL-адресов Sun для HTTP очень глючная. Нет резьбы обслуживания для закрытия незанятых соединений.

Другая большая проблема с keepalive заключается в том, что вам нужно удалять ответы. В противном случае соединение также будет потеряно. Большинство людей неправильно обрабатывают поток ошибок. Пожалуйста, смотрите мой ответ на этот вопрос для примера о том, как правильно читать ответы об ошибках,

HttpURLConnection.getResponseCode () возвращает -1 при втором вызове

4 голосов
/ 20 декабря 2009

С здесь :

Текущая реализация не буферизует тело ответа. Это означает, что приложение должно завершить чтение тела ответа или вызвать close (), чтобы отказаться от остальной части тела ответа, чтобы это соединение можно было повторно использовать. Кроме того, текущая реализация не будет пытаться читать блок при очистке соединения, то есть, если весь текст ответа недоступен, соединение не будет использоваться повторно.

Я читаю это так, как будто ваше решение должно работать, но вы также можете свободно звонить и соединение будет по-прежнему использоваться.

3 голосов
/ 06 января 2010

Ссылка , на которую ссылается disown, действительно помогла.

Мы знаем, что Apache HttpClient лучше, но для этого потребуется еще один jar, и мы могли бы использовать этот код в апплете.

Звонить 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);
}
...