У меня есть небольшое приложение, которое просто опрашивает сервер с помощью Jetty v9.2, HttpClient. Через несколько дней приложение зависнет. Первоначально мы определили, что пул потоков должен быть увеличен в размере, чтобы уменьшить падение производительности. Это изменение восстановило производительность в течение нескольких дней. Блокада осталась. Причина была изолирована от вызовов HTTP GET (проблема исчезает, если мы закомментируем метод).
Основная причина, которая лежит в основе Jetty HttpClient Управление соединениями или Управление потоками. Обычно Jetty HttpClient создает набор потоков для обработки HTTP GET (см. ниже), эти потоки возникают и исчезают, как и следовало ожидать. Примерно через 40 часов работы JDK VisualVM показывает как минимум 9 потоков подключения, которые не не исчезают немедленно:
- HttpClient - планировщик x 1
- HttpClient — клиент-селектор SectorManager x 4
- HTTP-клиент x 4
также
- RMI TCP-соединение
Всего девять или десять потоков. При следующем чтении создаются новые экземпляры потока для выполнения нагрузки, и клиент продолжает работу. Кроме того приложение. имеет часы с выделенным потоком, который продолжает работать после блокировки приложения, что указывает на то, что JVM, операционная система и сама машина в порядке.
Иногда мы видим, что эти «зависшие» потоки задерживаются до часа, прежде чем они исчезают из отображения потока VisualVM. По крайней мере, через 36 часов мы видим, что потоки остаются, и мы не видим, чтобы они исчезли.
Через несколько дней программа зависает. Указанное объяснение — утечка экземпляров потока, которые не были очищены. появляется приложение. заканчивается потоки и не может больше работать. Это, безусловно, останавливает HTTP GET, о чем свидетельствуют серверные журналы.
Основной HTTP-вызов использует приведенный ниже код, метод HttpClient GET:
/**
* GET
* @return null or string returned from server
**/
public static String get( final String command ){
String rslt = null;
final String reqStr = "http://www.google.com"; // (any url)
HttpClient httpClient = new HttpClient();
Request request;
ContentResponse response;
try {
//-- Start HttpClient
httpClient.start();
request = httpClient.newRequest( reqStr );
response = request.send();
if( null == response ){
LOG.error( "NULL returned from previous HTTP request.");
}
else {
if( (501 == response.getStatus()) || (502 == response.getStatus()) ){
setNetworkUnavailable(String.format("HTTP Server error: %d", response.getStatus() ));
}
else {
if( 404 == response.getStatus() ){
Util.puts(LOG,"HTTP Server error: 404");
// ignore message since we are talking to an old server
}
else if( 200 == response.getStatus() ){
rslt = response.getContentAsString();
}
else {
LOG.error(String.format( " * Response status: \"%03d\".", response.getStatus() ));
}
setNetworkAvailable();
}
}
}
catch ( InterruptedException iEx ){
LOG.warn( "InterruptException processing: "+reqStr, iEx );
}
catch ( Exception ex ){
Throwable cause = eEx.getCause();
if( (cause instanceof NoRouteToHostException) ||
(cause instanceof EOFException) ||
(cause instanceof SocketException)
&& cause.getMessage().startsWith( EX_NETWORK_UNREACHABLE ) ){
setNetworkUnavailable( cause.getMessage() );
}
else {
LOG.error( "Exception on: "+command, ex );
}
}
finally {
try {
httpClient.stop();
}
catch ( Exception ex ){
LOG.error( "Exception httpClient.stop(), ServerManager::get()", ex );
}
}
return rslt;
}//get method
Это основано на простых примерах, мало подробностей об использовании HttpClient. Все ли сделано по Хойлу?
При разных запусках мы также видим следующие исключения и сообщения журнала:
- [36822522] WARN 2014-Sep-02 02:46:28.464> HttpClient@2116772232{СТОПИНГ,8‹=0‹=200,i=0,q=0} Не удалось остановить поток[HttpClient@2116772232-729770,5 ,]
Интересно, относится ли это сообщение к одной из зависших тем? Или это сообщение указывает на отдельную и другую проблему, которую нам нужно изучить? Также:
- java.util.concurrent.TimeoutException (ExecutionException)
Похоже, это исключение тайм-аута потока. Какая нить хоть? Относится ли это к потокам HTTP-соединений? Я думаю, что как минимум, когда службы обнаруживают ошибки внутри, они могут, по крайней мере, указать местоположение ошибки и трассировку стека.
Есть очевидные вопросы:
- Написан ли код метода get() так, как требуется, чтобы не было утечек или не оставалось зависших ресурсов для кода Jetty HttpClient?
- How can we catch the warning: "Couldn't stop Thread" error?
- What is the impact of this error? Is there a way to 'smash' a thread stuck like that?
- Это как-то связано с 10 висящими нитями подключения? Есть только одно предупреждающее сообщение.
- Можно представить, что зависшая нить требует метки ERROR, а не предупреждения.
- Существует ли процесс обнаружения ошибок потоков и ошибок в целом в Jetty HttpClient?
- What attributes are available for the HttpClient to tune the service?
- Are there settings we can use to directly influence the thread-locking?
- Какие атрибуты доступны в среде или контексте HttpClient для управления настройкой службы?
- Can the Jetty HttpClient be restarted / rebooted or just stopped?
- Jetty calls are only made in the GET method shown (albeit with more logging, etc.)
- Является ли поток RMI частью вызовов Jetty HttpClient?
Еще одно наблюдение заключается в том, что когда мы «застреваем» потоки в VisualVM, он показывает избыточные потоки демона на панели «Потоки», а не увеличение количества потоков, не являющихся демонами.
Запуск кода, показанного выше, в цикле for в течение примерно 3 или 4 часов с 250-миллисекундным перерывом между вызовами HttpClient send() показывает утечку потока — ее легко воспроизвести в Linux. Выходные данные журнала не показывают предупреждений и только две ошибки тайм-аута в сети на расстоянии не менее 30 минут от утечки потока.
Предложения, наблюдения, улучшения и ответы приветствуются. Заранее благодарим.
Похожие вопросы:
Эти вопросы охватывают некоторые очень похожие моменты