Как я могу убедиться, что мой HttpClient 4.1 не пропускает сокеты? - PullRequest
12 голосов
/ 18 января 2011

Мой сервер использует данные из внутреннего веб-сервиса для построения своего ответа на основе запроса.Я использую Apache HttpClient 4.1 для выполнения запросов.Каждый первоначальный запрос приведет к 30 запросам к веб-сервису.Из них 4 - 8 будут заканчиваться сокетами, застрявшими в CLOSE_WAIT, которые никогда не будут освобождены.В конечном итоге эти застрявшие сокеты превышают мой ulimit, и мой процесс исчерпывает файловые дескрипторы.

Я не хочу просто поднимать свой ulimit (1024), потому что это просто замаскирует проблему.

Причина, по которой я перешел на HttpClient, заключается в том, что java.net.HttpUrlConnection вел себя так же.

Я попытался перейти к SingleClientConnManager для каждого запроса и вызвать для него client.getConnectionManager (). Shutdown (), но сокеты по-прежнему застряли.

Должен ли я попытаться решить эту проблему, чтобы в итоге я получил 0 открытых сокетов, когда нет запущенных запросов, или я должен сосредоточиться на сохранении и пуле запросов?

Для ясности я включил некоторые детали, которые могут иметь отношение к делу:

ОС: Ubuntu 10.10

JRE: 1.6.0_22

Язык: Scala 2.8

Пример кода:

val cleaner = Executors.newScheduledThreadPool(1) 
private val client = {
    val ssl_ctx = SSLContext.getInstance("TLS")
    val managers = Array[TrustManager](TrustingTrustManager)
    ssl_ctx.init(null, managers, new java.security.SecureRandom())
    val sslSf = new org.apache.http.conn.ssl.SSLSocketFactory(ssl_ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
    val schemeRegistry = new SchemeRegistry()
    schemeRegistry.register(new Scheme("https", 443, sslSf))
    val connection = new ThreadSafeClientConnManager(schemeRegistry)
    object clean extends Runnable{ 
        override def run = {
            connection.closeExpiredConnections
            connection.closeIdleConnections(30, SECONDS)
        }
    }
    cleaner.scheduleAtFixedRate(clean,10,10,SECONDS)
    val httpClient = new DefaultHttpClient(connection)
    httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), new UsernamePasswordCredentials(username,password))
    httpClient
}
val get = new HttpGet(uri)
val entity = client.execute(get).getEntity
val stream = entity.getContent
val justForTheExample = IOUtils.toString(stream)
stream.close()

Тест: netstat -a |grep {myInternalWebServiceName} |grep CLOSE_WAIT

(Перечисляет сокеты для моего процесса, которые находятся в состоянии CLOSE_WAIT)

Обсуждение с комментарием:

Этот код теперь демонстрирует правильное использование.

Ответы [ 3 ]

9 голосов
/ 18 января 2011

Необходимо заблаговременно извлекать истекшие / незанятые соединения из пула соединений, поскольку в блокирующей модели ввода-вывода соединения не могут реагировать на события ввода-вывода , если они не считываются / записываются в,Подробнее см.

http://hc.apache.org/httpcomponents-client-dev/tutorial/html/connmgmt.html#d4e631

2 голосов
/ 09 октября 2015

У меня была та же проблема, и я решил ее, используя предложенное здесь предложение: здесь .Автор затрагивает некоторые основы TCP:

Когда соединение TCP собирается закрыться, его завершение согласовывается обеими сторонами.Думайте об этом как о нарушении контракта цивилизованным способом.Обе стороны подписывают бумагу, и все хорошо.В разговоре о гике это делается с помощью сообщений FIN / ACK.Сторона A отправляет сообщение FIN, чтобы указать, что она хочет закрыть сокет.Сторона B отправляет ACK, говоря, что получила сообщение и рассматривает требование.Затем сторона B очищает и отправляет FIN для стороны A. Сторона A отвечает ACK, и все уходят.

Проблема возникает, когда B не отправляет свой FIN.А вроде застрял в ожидании этого.Он инициировал свою последовательность завершения и ждет, пока другая сторона сделает то же самое.

Затем он упоминает RFC 2616, 14.10 , чтобы предложить установить заголовок http для решения этой проблемы.Проблема:

postMethod.addHeader("Connection", "close");

Честно говоря, я действительно не знаю последствий установки этого заголовка.Но это остановило CLOSE_WAIT в моих модульных тестах.

2 голосов
/ 19 января 2011

Я пометил ответ Олега как правильный, поскольку он подчеркивает важный момент использования пула соединений HttpClient.

Однако, чтобы ответить на мой конкретный оригинальный вопрос, который был «Должен ли я пытаться решить для 0неиспользуемые сокеты или попытка максимизировать пул? "

Теперь, когда решение для пулов установлено и работает правильно, пропускная способность приложения увеличилась примерно на 150%.Я связываю это с тем, что нет необходимости пересматривать SSL и множественные рукопожатия, вместо этого повторно использовать постоянные соединения в соответствии с HTTP 1.1.

Определенно стоит работать, чтобы использовать пул по назначению, а не пытаться взломать с помощью вызова ThreadSafeClientConnManager.shutdown () после каждого запроса и так далее.Если, с другой стороны, вы вызывали произвольные хосты и не использовали маршруты повторно, как я, вы могли бы легко обнаружить, что возникает необходимость в такого рода хакерских атаках, поскольку JVM может удивить вас долгим сроком службы сокетов, назначенных CLOSE_WAIT, есливы не очень часто собираете мусор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...