как сохранить несколько, Java HttpConnections открывается тому же месту назначения

Мы используем 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 () абсолютно близко соединение и удалять его из пула?

6
задан Lightbeard 20 December 2009 в 20:16
поделиться

3 ответа

Из здесь :

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

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

4
ответ дан 9 December 2019 в 20:44
поделиться

У нас была эта проблема также на Java 5, и наше решение состоит в том, чтобы переключиться на Apache HttpClient с объединенным диспетчером соединений.

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

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

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

4
ответ дан 9 December 2019 в 20:44
поделиться

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

Мы знаем, что 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);
}
4
ответ дан 9 December 2019 в 20:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: