Как стать автором
Обновить

Комментарии 10

По-моему ваш код не скомпилируется.

И еще, что интереснее: вы предполагаете, что сообщения (то есть файлы) будут постоянно храниться в очереди MQ? Если нет, то как они туда попадают, когда клиент запрашивает download?

Мне кажется, что вы обещали какое-то приложение, которое будет хранить файлы, и помещать их в очередь по запросу, но не показали его. А в возможности его реализации есть некоторые сомнения.
Не вникал в детали, но думаю, что у автора все получится (сам делал что то подобное дважды, лет 10 назад — одно решение было очень похоже: большой дамп разбивался на мелкие SOAP-запросы для преодоления файрволов и сети плохого качества, куски дампа пападали в JMS-очередь, откуда перемещались во временную папку, при наличии всех кусков они собирались в файл и раздампливались).
>Не вникал в детали
А вы вникните. Еще раз повторю — автор при выполнении download берет файл из очереди MQ. На первый взгляд, есть два варианта:

  • он там хранится
  • он туда попадает после того, как REST сервер пошлет запрос приложению, и оно файл в очередь загрузит.

Оба способа имеют очевидные недостатки.

P.S. Судя по вашему описанию, у вас не было REST клиента, который выгружал бы файлы из системы. А это две большие разницы.
Прошу прощения если ввел в заблуждение.

Задача статьи состоит в демонстрации способа потоковой передачи файла между REST сервисом и системой обмена сообщениями (MQ). Описанный вначале статьи сценарий служит для иллюстрации ситуации, в которой может возникнуть такая потребность.

Чтобы не усложнять статью и не отвлекаться от её основной задачи был приведен код сервиса, выполняющего минимальный набор операций, позволяющих продемонстрировать каким образом осуществляется потоковое сохранение файла из REST запроса в очередь MQ, а также как можно вернуть в REST-ответ файл, хранящийся в MQ очереди.

В рамках демонстрационного сервиса, метод upload получает файл от клиента и сохраняет его в MQ очередь, в которой эти сообщения дальше никуда не отправляются. Метод download считывает эти сообщения и возвращает в виде ответа клиенту.

В промышленном сценарии сообщения отправляются в целевую сеть, где считываются приложением и записываются в хранилище, например, в систему электронного документооборота, после чего возвращают клиенту, например, идентификатор файла. При запросе файла, клиент передает этот идентификатор REST сервису, который отправляет его через систему обмена сообщениями в приложение, которое считывает из хранилища этот файл и помещает его в MQ, после чего REST-сервис отправляет полученный набор сообщений из MQ в REST ответа в виде файла.

Если более детально рассматривать промышленный сценарий, то это могло бы выглядеть так:
Сценарий отправки файла от клиента:
  1. Клиент отправляет файл в REST сервис.
  2. REST сервис в потоковом режиме сохраняет файл в очередь MQ «CONTENT.TO.STORE» в виде набора сообщений.
  3. REST сервис формирует сообщение, указывая мета информацию о файле (например, наименование, размер, идентификатор группы сообщений), и отправляет в очередь MQ «REQUEST.TO.STORE».
  4. MQ передает эти сообщения в целевые очереди, находящуюся в территориально удаленной сети.
  5. На той стороне приложение Application прослушивает очередь «REQUEST.TO.STORE» (например, через Message-Driven Bean), получая мета информацию о пересланном файле.
  6. Приложение по полученному идентификатору группы сообщений считывает из очереди «CONTENT.TO.STORE» содержимое файла и сохраняет его в своем хранилище.
  7. Приложение формирует сообщение с идентификатором файла, которое было присвоено в этом хранилище, и отправляет его в очередь MQ «RESPONSE.TO.STORE».
  8. MQ передает это сообщение обратно в сеть, где размещен REST сервис, который ожидает ответного сообщения из очереди «RESPONSE.TO.STORE».
  9. REST сервис, получив ответ – возвращает клиенту идентификатор файла.


Сценарий запроса файла:
  1. Клиент отправляет в REST сервис идентификатор файла.
  2. REST сервис формирует сообщение, указывая полученный идентификатор файла и отправляет в очередь MQ «REQUEST.TO.RETRIEVE».
  3. MQ передает сообщение в сеть, где размещается приложение Application.
  4. Приложение Application, получив сообщение из очереди «REQUEST.TO.RETRIEVE», по полученному идентификатору считывает файл из хранилища и записывает в очередь MQ «CONTENT.TO.RETRIEVE».
  5. Приложение Application формирует сообщение с метаинформацией о файле (имя, размер, идентификатор группы сообщений) и отправляет в очередь «RESPONSE.TO.RETRIEVE».
  6. MQ передает эти сообщения обратно в сеть, где размещен REST сервис, ожидающий ответного сообщения из очереди «RESPONSE.TO.RETRIEVE».
  7. REST сервис считывает по идентификатору группы сообщений содержимое файла из очереди «CONTENT.TO.RETRIEVE» и отправляет в виде ответа клиенту.

Да, так уже понятно, что именно задумано. Только вот есть подозрения, что так работать будет не очень хорошо. Но это уже сплошная интуиция, тут я не настаиваю.

Дело в неизвестных задержках между выполнением запроса 1 (сценарий запроса файла) и получением ответа в п. 7. Между ними два обмена по MQ, причем второй — это передача самого файла (очевидно, фрагментами, примерно аналогично 1 сценарию).

Все это время ваш REST клиент очевидно ждет. И сервис тоже. Если учесть, что файлы могут быть разного размера, и нагрузка на сеть варьируется, настроить фиксированный таймаут, который бы хорошо работал во всех случаях, будет непросто.

Да, это не значит, что у меня есть готовое решение. Я бы наверное нотификацию клиенту сдалал бы асинхронно, на основе вебсокетов, например. И при ее получении уже загрузил бы сам файл.
какая цель данной статьи?
данный говнокод не пройдет маломальски вменяемое код ревью
Приходилось встречаться с решениями, в которых отсутствует именно потоковая передача файла, т.е. приложение полностью сохраняет файл в оперативную память прежде чем записать его в конечную систему (например, в MQ очередь). В результате, приложение накладывает ограничение на размер передаваемого файла, а загрузка ресурсов не только снижает пропускную способность, но и может привести к падению системы из-за нехватки памяти.

В частности, такое решение, как IBM Integration Bus до сих пор не позволяет в потоковом режиме сохранить получаемый файл по REST протоколу и записать его в MQ очередь.

В статье хотелось продемонстрировать способ, которым я воспользовался для решения такой задачи. Возможно, кому-то будет полезно.

Ой как все фиговенько!


Во-первых, как-то вы оптимистично обрабатываете нештатные ситуации. Если у вас оборвался InputStream от клиента, вы все-равно должны отослать в очередь пакет JMS_IBM_Last_Msg_In_Group и как-то пометить его как "Exception". В свою очередь reader, прочитав такой пакет, должен будет вместо нормального завершения сгенерить IOException и дать понять консьюмеру на другой стороне, что стрим оборвался.


Во-вторых, ваш reader будет работать только, если весь файл полностью уже лежит в очереди, потому как вы делаете receiveNoWait(). В качестве бонуса такая логика на ура будет выдавать недочитанные файлы, если writer не успел записать блок. Здесь нужно поставить обычный блокирующий read() с каким-нибудь таймаутом, чтобы исключить бесконечную блокировку, если нужный пакет так и не пришел.


В-третьих, приложение-консьюмер каким-то мистическим образом узнает groupId, который ему надо читать. В реале вам нужно сделать бесконечный reader, который постоянно слушает очередь, и сначала запрашивает первый пакет из любой цепочки, узнает из него groupId и уже потом дочитывает остальное из данной цепочки, а затем возвращается на начало. И чтобы расширить пропускную способность, надо бы сделать целый пул из таких reader-ов.


В-четвертых, очередь будет потихоньку забиваться пакетами из оборванных цепочек. Необходима периодическая очистка от старых пакетов.


Ну и наконец, если уж вы используете IBM MQ, то вроде там как бы уже реализован сервис файлтрансфера. А вы тут предлагаете решения для бедных...

Спасибо за развернутые замечания и предлагаемые решения!

Согласен с Вами, что приведенный код не отвечает требованиям по надежности в случае нештатных ситуаций. В статье я бы не хотел усложнять код обработкой такого рода ошибок, а как можно проще продемонстрировать библиотеки и классы, используемые для решения такой задачи.

Очень похоже на контрольную или практическую работу в ВУЗе по слогу и оформлению.
Не понял отчего это REST.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Публикации

Истории