Что может вызвать сокет ConnectException: истекло время ожидания соединения?
У нас есть клиент Webstart, который связывается с сервером, отправляя сериализованные объекты по HTTPS с помощью java.net.HttpsURLConnection
,
Все отлично работает на моей локальной машине и на тестовых серверах, расположенных в нашем офисе, но я испытываю очень, очень странную проблему, которая возникает только на наших производственных и промежуточных серверах (причем время от времени). Основное различие, которое я знаю между этими серверами и теми, которые находятся в нашем офисе, заключается в том, что они расположены в другом месте, и связь клиент-сервер с ними значительно медленнее, но до этого она работала в течение долгого времени в производстве.
Во всяком случае, вот что происходит:
- Клиент после настройки параметров, таких как время ожидания чтения, и таких свойств, как
Content-Type
наHttpURLConnection
, звонкиgetOutputStream()
на нем, чтобы получить поток для записи. - На данный момент, насколько я могу судить, клиент зависает в течение некоторого периода времени.
- Затем клиент генерирует следующее исключение:
java.net.ConnectException: истекло время ожидания соединения: соединение в java.net.PlainSocketImpl.socketConnect(собственный метод) на java.net.PlainSocketImpl.doConnect(неизвестный источник) на java.net.PlainSocketImpl.connectToAddress(неизвестный источник) на java.net.PlainSocketImpl.connect(неизвестный источник) на java.net.SocksSocketImpl.connect(неизвестный источник) на java.net.Socket.connect(неизвестный источник) на com.sun.net.ssl.internal.ssl.SSLSocketImpl.connect(неизвестный источник) на com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.connect(неизвестный источник) at sun.net.NetworkClient.doConnect(неизвестный источник) на sun.net.www.http.HttpClient.openServer(неизвестный источник) на sun.net.www.http.HttpClient.openServer(неизвестный источник) на sun.net.www.protocol.https.HttpsClient.(Неизвестный источник) на sun.net.www.protocol.https.HttpsClient.New(Неизвестный источник) на sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(неизвестный источник) на sun.net.www.protocol.http.HttpURLConnection.plainConnect(неизвестный источник) на sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(неизвестный источник) на sun.net.www.protocol.http.HttpURLConnection.getOutputStream(неизвестный источник) на sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(неизвестный источник)
Обратите внимание, что это не SocketTimeoutException
, который connect()
метод на HttpURLConnection
говорит, что сбрасывает, если время ожидания истекает до установления соединения. Кроме того, когда это происходит, я могу позвонить conn.getResponseCode()
и я получаю код ответа 200.
- На стороне сервера
EOFException
брошен вObjectInputStream
конструктор, который пытается прочитать заголовок сериализации, но терпит неудачу, потому что клиент никогда не получаетOutputStream
написать в.
Если это помогает, вот звонки, сделанные на HttpsURLConnection
до звонка getOutputStream()
(отредактировано, чтобы показать только сделанные вызовы, а не всю структуру кода, делающего это):
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setUseCaches(false);
conn.setReadTimeout(30000);
conn.setRequestProperty("Cookie", cookie);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-java-serialized-object");
conn.getOutputStream();
Дело в том, что я понятия не имею, как это может происходить, особенно с учетом того, что это происходит только изредка (нет четкой схемы действий, которую я могу сказать), и даже тогда, когда есть (относительно) высокая задержка между клиентом и сервер.
Учитывая то, что я смог найти до сих пор о java.net.ConnectException: Connect timed out
Мне было интересно, не была ли это какая-то проблема с сетью или брандмауэром в сети, на которой работают наши серверы... но это не имеет особого смысла для меня, учитывая, что запрос явно передается сервлету. Кроме того, другие приложения, работающие в той же сети, не сообщали о подобных проблемах.
Кто-нибудь знает, что может быть причиной этого, или даже что я должен расследовать?
1 ответ
Мы сталкивались с этим в аналогичном случае с вашим. Обычно при высокой нагрузке и непросто воспроизвести на тесте. Еще не исправили, но это шаги, которые мы прошли.
Если это проблема с брандмауэром, мы получим исключение Connection Refused или SocketTimeout.
1) Можете ли вы отслеживать эти запросы в журнале доступа на сервере - они показывают HTTP-статус 200 или 404 или что-то еще? В нашем случае журналы сервера (в данном случае IIS) показали, что клиент закрыл соединение, а не сервер. Так что это было загадкой.
Обновление: если клиент всегда получает 200, то сервер фактически отослал некоторый ответ, но я подозреваю, что размер байта ответа (если это записано в журналах доступа) покажет значение, отличное от обычного размера ответа для этот запрос.
Если он показывает тот же размер ответа, то у вас есть (возможно, не правдоподобное) условие, что сервер действительно ответил правильно, но клиент не получил ответ обратно, потому что соединение было разорвано где-то посередине.
2) Группы сетевых администраторов изучили трафик TCP/IP, чтобы определить, какой конец (или промежуточный маршрутизатор) завершает диалог HTTP / TCP-IP. И как только мы понимаем, с какой целью заканчивается соединение, стоит посмотреть, почему. Кто-то достаточно умелый может запустить Snoop
3) На сервере настроено / ограничено максимальное количество запросов - и это ограничивает ваши соединения?
4) Существуют ли промежуточные балансировщики нагрузки, при которых запросы могут быть отброшены?
Обновление: еще одна вещь, которую мы хотели, но не выполнили, - это создать статический маршрут между клиентом и сервером, чтобы уменьшить количество промежуточных интервалов и гарантировать, что нет связанных с сетью соединений. Смотрите http://en.wikipedia.org/wiki/Static_routing
5) Еще одно предложение - настроить ConnectTimeout, чтобы увидеть, работают ли они с более высоким значением. Обновление: вы можете попробовать conn.getErrorStream()
Возвращает поток ошибок, если соединение не удалось, но сервер тем не менее отправил полезные данные. Если соединение не было подключено, или если у сервера не было ошибки при подключении, или если сервер имел ошибку, но данные об ошибках не были отправлены, этот метод возвратит нуль.
6) Можно также попытаться получить набор дампов потоков на сервере с интервалом в 5 секунд, чтобы увидеть, показывает ли какой-либо поток эти входящие запросы на сервере.
Обновление: на сегодняшний день мы научились жить с этой проблемой, потому что мы насчитали 200-300 из 400000 запросов в день, что составляет 0,00075 %
У нас также случаются спорадические тайм-ауты при использовании его на наших серверах. Мы можем исправить это двумя вещами:
- Используйте определенный ContentLength через
setFixedLengthStreamingMode
(снизил количество ошибок с ~150 до 10) - Повторите попытку, если произойдет тайм-аут (частота ошибок от 10 до 0. После макс. Одной попытки все прошло)
псевдокод:
//set timeouts to 6s
try{
//open connection here and write etc.
//use a timeout of 6s (since retry is in place)
}
catch (java.io.InterruptedIOException e) {
//read- or connection time out try again
}
Другая теория, почему это происходит, может быть следующей:
В документации по HttpURLConnection/HttpsURLConnection можно прочитать следующее:
Каждый экземпляр HttpURLConnection используется для выполнения одного запроса, но базовое сетевое соединение с HTTP-сервером может прозрачно совместно использоваться другими экземплярами.
Итак, теперь звоню close()
только было бы хорошо, но и звонить disconnect()
завершит сокет для других пользователей / прозрачно разделяемых соединений, которые затем перейдут в SocketTimeOut по истечении периода тайм-аута.