2011-01-18 1 views
12

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

Я не хочу просто поднять мой 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)

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

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

ответ

8

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

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

+0

Спасибо за ваш ответ, я согласен с тем, что документация предполагает, что это должна быть эффективная мера. Количество сокетов, потерянных в CLOSE_WAIT, по-прежнему надежно растет с использованием этого очистителя вне потоковой передачи, поэтому он не был эффективным. Я добавил некоторые детали реализации к моему вопросу. –

+0

Я отказываюсь. Я не понял, что в самом коде приложения я делал дополнительные образы изображений для выполнения бизнес-логики. Они все еще использовали методы WS.url до введения HttpClient и оставляли сокеты позади. –

+0

Сломанная ссылка. (Пожалуйста, укажите названия и описания со ссылками.) Может быть, это одна и та же информация? http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html – danorton

2

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

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

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

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

1

У меня была такая же проблема, и я решил использовать ее, предлагая найти здесь: here. Автор затрагивает некоторые основы 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 на моих модульных тестах.

Смежные вопросы