Подтвердить что ты не робот

Как я могу убедиться, что мой HttpClient 4.1 не утечки сокетов?

Мой сервер использует данные из внутренней веб-службы для построения своего ответа на основе запроса. Я использую 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)

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

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

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

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