понедельник, 7 марта 2011 г.

Ошибка java.net.SocketTimeoutException, IE и Keep-Alive


Вот уже несколько дней мне не давала покоя ошибка, возникающая при обработке запросов в моем небольшом сервисе, написанном на Java servlet, и работающая в Apache Tomcat. Ошибка была очень странная, проявлялась в случайные моменты времени, связи между которыми я так и не нашел.
Ошибка оставляла такую запись в логах:
java.net.SocketTimeoutException: read timeout
Кусок кода, где происходит ошибка:

BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), ENC));
char[] buf = new char[1024];
int buf_len = in.read(buf);

Этот участок кода читает входящий запрос как поток и записывает его во временный буфер. Ошибка возникает на вызове in.read(buf), и имеет странный спонтанный характер. Т.е. имеется факт входящего запроса, не содержащего данных.
Забыл сказать, что в силу определенных условий на клиентами были браузеры Internet Explorer. Скрипт javascript, используя технику AJAX, делал запросы моего сервиса.
Были разные попытки выяснить причину, например, тщательное журналирование запросов-ответов. В этом мне очень помог вот этот фильтр: Dump request and response using javax.servlet.Filter.
Тем не менее, дампы запросов ошибку не выявили. После очередного сеанса поиска в гугле отыскалось очень интересное обсуждение похожей проблемы: RPC SocketTimeoutException: Exception while dispatching incoming RPC call. Вырисовывалась закономерность: использовался IE и ajax-запросы. В дискуссии были интересные замечания:

Apparently there's some IE bug here?  I can't figure out if it's related to keep-alive, 
POST, SSL, or some combo of all three.  GWT does use POST for RPC, but I doubt this is 
GWT-specific, and either way I think more research is needed maybe.

а также:

After several days of study and code changes, we are having good success by adding this to the jboss server.xml Connector element: maxKeepAliveRequests="1".  The JBOSS manual says ... "Setting this attribute to 1 will disable HTTP/1.0 keep-alive, as well as HTTP/1.1 keep-alive and pipelining."  Using firebug, we can see differences in the http response header telling the browser (IE7) to close the connection.  So far, no more socket exceptions.  Yeah!!


Ну что же, изменив в настройке сервера параметр maxKeepAliveRequests="1" ошибка исчезла! Ура!


Небольшое пояснение о keep-alive: если кто не знает, начиная с версии протокола http 1.1 появилась возможность не открывать каждый раз соединение с сервером при очередном запросе. Соединение открывается при первом запросе и не закрывается некоторое время (определяется настройками сервера). Это позволяет избежать накладных расход на открытие-закрытие физического соединения. Так вот, параметр Tomcat-а maxKeepAliveRequests как раз регулирует максимальное количество идущих друг за другом запросов с сохранением соединения открытым (затем сервер принудительно закрывает соединение и очередному запросу придется открыть его вновь).

maxKeepAliveRequests="1" фактически отключает режим keep-alive. Конечно, это не очень хороший выход, но в случае с IE ничего не остается делать.

Можеть кто-нибудб знает глубинную причину происходящего? Пишите!

Вот еще немного пояснений, зачем нужен keep-alive: Keep-alive (persistent) connections with Java Servlets


Удачи.

Комментариев нет:

Отправить комментарий