Как обработать полнодуплексный HTTP POST, используя Jersey 1.x или другую клиентскую библиотеку HTTP?
Мне нужна помощь в проекте, над которым я работаю. Это сама библиотека, использующая Jersey 1.x (1.19.1), нацеленная на HTTP-публикацию документа JSON и получение соответствующего ответа JSON от сервера.
Я сталкиваюсь с проблемой, когда ответ от сервера "большой". Документ JSON, опубликованный моим клиентским приложением, содержит несколько заданий, которые должны быть выполнены сервером, а документ JSON, отправленный обратно сервером, состоит из выходных данных этих заданий. Рабочие места можно считать независимыми друг от друга. Сервер работает в потоковом режиме, что означает, что он начинает обрабатывать задания до того, как получает весь документ JSON, опубликованный клиентом. И он начинает отправлять результаты работ, как только они завершены. Таким образом, сервер начинает отвечать на мое клиентское приложение, пока оно еще отправляет запрос. Здесь моя проблема. Когда запрос становится большим и получает ответ (больше заданий), мое приложение останавливается и в какой-то момент завершается.
Я потратил много времени, пытаясь выяснить, что происходит, и вот что найдено и что я вывел.
Джерси, для обработки HTTP-коммуникации используется класс из JDK (в rt.jar). Я забыл точное имя и сейчас не имею доступа к своей работе, но назовем его HttpConnection. В этом классе есть метод checkError(), который вызывается и выдает IOException только с сообщением о невозможности записи на сервер. Отладка Я смог понять, что для атрибута этого класса с именем Trouble было установлено значение true, потому что метод write() ранее перехватывал IOException. checkError() генерирует IOException на основе этого логического флага проблемы. Невозможно легко увидеть причину IOException, потому что классы JRE скомпилированы без символов отладки, но мне удалось увидеть, что это IOExeption было проблемой "сброса соединения по одноранговой сети".
Затем я попытался понять, почему сервер сбрасывает соединение. Я использовал HTTP-прокси, который захватывает HTTP-трафик между моим клиентским приложением и сервером, но это не дало мне никаких подсказок, даже кажется, что прокси-сервер не может должным образом обрабатывать соединение с сервером!
Поэтому я попытался использовать Wireshark для захвата трафика и посмотреть, что не так. Вот что я нашел.
На стороне клиента отправляются пакеты, соответствующие посту документа JSON запроса, и вскоре после этого сервер начинает отвечать, как описано выше. На стороне сервера отправляется все больше и больше пакетов, и я заметил, что буфер уровня TCP (называемый окном TCP в Wireshark) на стороне клиента имеет размер, который уменьшается все больше и больше по мере того, как сервер отправляет пакеты. Пока он не станет полным (размер: 0 байт). Таким образом, уровень TCP на стороне сервера больше не может отправлять данные на уровень TCP на стороне клиента и, таким образом, также становится заполненным. Разговор, в конце концов, касается только попыток отправки данных с обеих сторон, которые снова и снова заканчиваются неудачей. В конечном итоге сервер решает отправить пакет сброса. Это соответствует причине IOExcpetion, которую я упомянул выше, я считаю.
Насколько я понимаю: до тех пор, пока сервер не начнет передавать ответ, все в порядке. Когда сервер начинает отправлять ответ, буфер TCP на стороне клиента начинает заполняться. Но поскольку клиентское приложение еще не читает ответ, содержимое этого буфера не используется. Когда сервер отправил достаточно данных для заполнения этого буфера, он больше не может отправлять данные, и буфер его уровня TCP также переполняется, потому что сервер продолжает отправлять данные. В результате клиентское приложение не может завершить отправку запроса JSON-документа. Связь заблокирована с обеих сторон, и сервер решает сбросить соединение.
Мой вывод таков: код, как написано в данный момент, не поддерживает такую полнодуплексную связь, потому что ответ от сервера не используется так, как он получен. Действительно, прогуливаясь по коду Джерси, который выполняется моей библиотекой путем отладки, становится ясно, что шаблон:
- первый: connection.getOutputStream().write()
- а затем: response.getInputStream().read()
По моему мнению, основная причина проблемы заключается в том, что библиотека, над которой я работаю, использует Джерси синхронно, что не совсем подходит для работы сервера (потоковая передача ответа во время отправки запроса).
Я много искал в Интернете решение, сохранившее Джерси 1.19.1 для меня, чтобы улучшить библиотеку с минимальным воздействием, но я потерпел неудачу. Это причина, почему я прошу помощи здесь и сейчас;)
По сути, мой вопрос таков: возможно ли сделать то, что мне нужно, сохраняя клиентскую библиотеку Джерси 1.19.1, и если да, то как? Если нет, то какую клиентскую библиотеку HTTP я должен использовать для своей библиотеки (чтобы написать пост-запрос и одновременно прочитать соответствующий ответ), и если бы вы могли дать мне базовый пример, чтобы я мог быть в курсе, это было бы очень полезно,
И последнее: curl работает просто отлично, я могу полностью опубликовать тот же документ JSON и получить ответ, используя его, поэтому на стороне сервера проблем нет, как я подозревал в самом начале моего исследования. И это хорошо масштабируется (я пытался отправить огромные документы JSON). Конечно, я убедился, что HTTP-заголовок поста одинаков в случае моей библиотеки и в случае curl.
Большое спасибо за чтение меня и за ваши ответы.
С наилучшими пожеланиями,
Лоик