Способы буферного ответа REST
Там есть конечная точка REST, которая обслуживает большие (десятки гигабайт) куски данных для моего приложения.
Приложение обрабатывает данные в своем собственном темпе, и по мере роста объемов входящих данных я начинаю использовать тайм-аут конечной точки REST.
Значение, скорость обработки меньше, чем пропускная способность сети.
К сожалению, нет возможности повысить скорость обработки, так как нет "достаточно" - объемы входящих данных могут расти неограниченно.
Я думаю о способе хранения входящих данных локально перед обработкой, чтобы освободить соединение конечной точки REST до истечения тайм-аута.
До сих пор я до сих пор загружал входящие данные во временный файл и считывал (обрабатывал) указанный файл одновременно с помощью OutputStream/InputStream.
Сортировка буферизации с использованием файла.
Это создает собственные проблемы:
- что, если скорость обработки будет быстрее, чем скорость загрузки в течение некоторого времени, и я получу EOF?
- файловый парсер работает с ObjectInputStream и ведет себя странно в случае пустого файла /EOF
- и так далее
Существуют ли обычные способы сделать это?
Существуют ли альтернативные решения?
Просьба дать некоторые рекомендации.
Upd:
Я хотел бы отметить: http-сервер не в моем распоряжении.
Предположите, что это поставщик данных поставщика. У них много потребителей и они отказываются что-то менять только для одного.
Похоже, мы единственные, кто использует все свои данные, так как скорость обработки клиентских приложений намного больше, чем их примерные показатели производительности клиента. Тем не менее, мы не можем сопоставить нашу производительность приложения с пропускной способностью сети.
Сервер не поддерживает запросы HTTP-диапазона или разбиение на страницы.
Невозможно разделить данные в кусках, чтобы не загружать, так как нет атрибута фильтрации, чтобы гарантировать, что каждый кусок будет достаточно маленьким.
В ближайшее время: мы можем загрузить все данные за определенный период времени до истечения тайм-аута, но не можем его обработать.
Наличие адаптера между входным потоком и outpustream для pefrorm в качестве блокирующей очереди поможет тонну.
Ответы
Ответ 1
Вы используете что-то вроде new ObjectInputStream(new FileInputStream(..._)
а решение для EOF может быть FileInputStream
сначала в WriterAwareStream
которое блокировалось бы при попадании EOF до тех пор, пока писатель пишет.
Во всяком случае, в случае, если задержка не имеет большого значения, я бы не стал начинать обработку до завершения загрузки. Зачастую, вы не можете сделать с неполным списком объектов.
Возможно, вам может помочь некоторая очередь с памятью на основе файлов, например, Chronicle-Queue. Это быстрее, чем работать с файлами напрямую и может быть еще проще в использовании.
Вы также можете реализовать HugeBufferingInputStream
внутренне, используя очередь, которая считывает из своего входного потока, и, в случае, если у него много данных, он выплевывает их на диск. Это может быть хорошая абстракция, полностью скрывающая буферизацию.
Там также FileBackedOutputStream
в FileBackedOutputStream
, автоматически переходя от использования памяти к использованию файла при получении большого объема, но я боюсь, он оптимизирован для небольших размеров (с ожиданием десятков гигабайт, нет смысла пытаться использовать память).
Ответ 2
Существуют ли альтернативные решения?
Если ваш потребитель (клиент-клиент) испытывает затруднения с потоком данных, вы можете посмотреть на дизайн, в котором клиент управляет своей собственной работой, вытягивая данные с сервера по требованию.
RFC 7233 описывает запросы диапазона
устройства с ограниченным локальным хранилищем могут извлечь выгоду из возможности запросить только подмножество большего представления, например, одну страницу очень большого документа или размеры встроенного изображения
Запросы диапазона HTTP на веб-сайте MDN могут быть более доступными.
Ответ 3
Это то, для чего предназначены серверы очередей. RabbitMQ, Kafka, Kinesis, любой из них. Возможно, KStream будет работать. Со всем, что вы получаете с HTTP-сервера (с учетом вашего ограничения, что он не может быть разбит на части работы), вы можете разбить его на куски байтов некоторого разумного размера, возможно, 1024 КБ. Ваше приложение будет нажимать/публиковать эти записи/сообщения в теме/очереди. Все они будут иметь общий идентификатор серии, чтобы вы знали, какие куски совпадают, и каждый из них должен иметь порядковый номер, чтобы их можно было вернуть обратно в правильном порядке; с одним разделом Kafka, вы, вероятно, могли бы полагаться на смещения. Вы можете опубликовать окончательную запись для этой серии с флагом "done", который будет действовать как EOF для того, что его потребляет. Конечно, вы отправляете HTTP-ответ, как только все данные будут поставлены в очередь, хотя это может быть необязательно еще обработано.
Ответ 4
не уверен, что это поможет в вашем случае, потому что вы не указали, какая структура и формат данных приходят вам, однако я возьму красиво нормированный, глубоко вложенный иерархический xml (т.е. в значительной степени худший случай для потоковое, правда?... pega bix?)
Я предлагаю частичное решение, которое может позволить вам обойти ограничение того, что вы не сможете контролировать, как ваш клиент взаимодействует с сервером данных http -
-
развертывайте свой собственный веб-сервер в любой современной технологии, которой вы управляете, - ваш локальный сервер будет сидеть перед вашей локальной кешированной копией данных
-
периодически загружайте выходные данные веб-службы с помощью встроенной библиотеки запросов HTTP, используемой в командной строке, такой как aria2c
curl
wget
et. al, etl (или что угодно) непосредственно на локальный файл с XML файлом. Это происходит так часто, как нужно
-
укажите своего клиента на свой собственный сервер 127.0.0.1/modern_gigabyte_large/get...
"умный" сервер, вместо старого сервера api.vendor.com/last_tested_on_megabytes/get...
некоторые мысли:
-
вам может потребоваться реорганизовать вашу модель данных, чтобы указать, что данные веб-службы xml, которые вы и ваши клиенты потребляете, были датированы при последнем успешном запуске ^ (т.е. обновите эту дату при завершении следующего процесса)
-
теоретически было бы возможно, чтобы вы преобразовали базовый xml на пути к лучшему представлению записей потоковым способом вашему клиенту webservice (если вы еще этого не сделали), но это потребует усилий - я мог бы обсудить это больше, если была предоставлена выборка структуры данных
-
вся эта работа может выполняться параллельно с вашим существующим приложением, которое продолжается в вашей последней версии успешно обработанных "старых данных" до тех пор, пока не появятся новые данные новой версии
^ в торговле вам теперь нужно будет управлять "скользящим окном" файлов данных, где каждый "результат" - это конкретный экземпляр вашего приложения, загружающий данные webservice и сохраняющий его на диске, а затем успешно глотает его в вашей модели:
-
последний (два?) хороший результат (-ы) сжатый (по моему опыту, гигабайты xml упаковывают вниз часть helluva)
-
следующий ожидающий/предварительный результат во время потоковой передачи на диск/выполнение проверки целостности/проглатывания данных - (это становится текущим "хорошим" результатом, а последний "хороший" результат становится результатом "предыдущего хорошего")
-
если мы предположим, что вы глотаете реляционные db, текущие (и, возможно, предыдущие) tables
с данными webservice, загруженными в ваше приложение, и следующую ожидающую table
-
их переключение становится операцией метаданных, но теперь ваша база данных должна хранить как минимум данные webservice x2 (или x3 - все, что подходит вашим ограничениям)
-
... да, вам не нужно это делать, но вы пожелаете, чтобы вы сделали это, когда что-то пошло не так :)
Похоже, мы единственные, кто использует все свои данные
- это означает, что есть какой-то способ разбить или ограничить фид webservice - как другие клиенты дискриминируют, чтобы не получать полную monty?
Ответ 5
Вы можете использовать методы кэширования в памяти или использовать потоки Java 8. Для получения дополнительной информации см. Следующую ссылку: https://www.conductor.com/nightlight/using-java-8-streams-to-process-large-amounts-of-data/
Ответ 6
Возможно, Camel может помочь вам регулировать сетевую нагрузку между продюсером и продюсером REST?
Например, вы можете ввести конечную точку Camel, выступающую в качестве прокси-сервера перед реальной конечной точкой REST, применить некоторую политику дросселирования, прежде чем переходить на реальную конечную точку:
from (" http://localhost: 8081/mywebserviceproxy ").throttle(...).to(" http://myserver.com:8080/myrealwebservice);
http://camel.apache.org/throttler.html http://camel.apache.org/route-throttling-example.html
Мои 2 цента,
Бернард.
Ответ 7
Если у вас достаточно памяти, возможно, вы можете использовать хранилище данных в памяти, такое как Redis.
-
Когда вы получаете данные из конечной точки Rest, вы можете сохранить свои данные в списке Redis (или любой другой структуре данных, которая вам подходит).
-
Ваш потребитель будет потреблять данные из списка.