Реактивный потоковый подход при загрузке файлов в Spring (загрузка) - PullRequest
2 голосов
/ 02 марта 2020

Мы потратили много часов на inet и на стекопоток, но ни один из выводов не удовлетворил нас тем, как мы планировали загрузку файлов в контексте Spring.

Несколько слов о нашей архитектуре. У нас есть клиент node.js, который загружает файлы в приложение Spring Boot. Давайте назовем эту конечную точку REST нашей «конечной точкой клиента». Наше приложение Spring Boot действует как промежуточное программное обеспечение и называет конечные точки «сторонней системы», поэтому мы называем эту конечную точку «чужой» из-за различий. Основной целью является обработка файлов между этими двумя конечными точками и между ними между ними некоторые бизнес-логики c.

На самом деле интерфейс нашего клиента выглядит следующим образом:

public class FileDO {

    private String id;
    private byte[] file;
    ...
}

Здесь мы находимся очень гибкий, потому что это наш клиент и определение нашего интерфейса.

Из-за проблемы, из-за которой нашей системе иногда не хватает памяти, мы планируем реорганизовать наш код в более потоковый, реактивный подход. Когда я пишу «под нагрузкой», я имею в виду тяжелую нагрузку, например, сотни одновременных загрузок файлов с большими файлами размером от нескольких МБ до 1 ГБ. Мы знаем, что эти тесты не представляют реальные случаи использования приложений, но мы хотим быть готовыми.

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

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

Итак, каковы наши шаги на данный момент? Мы представили новый интерфейс клиента (node.js -> Spring Boot), как показано ниже. Это работает до сих пор. Но действительно ли это потоковый подход? Первые метрики показали, что это не уменьшает использование памяти.

@PostMapping(value="/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public Mono<Void> upload(@RequestPart(name = "id") String id, @RequestPart(name = "file") Mono<FilePart> file) {
    fileService.save(id, file);
    ...
}

Первый вопрос: этот тип Mono <> здесь? Или нам лучше иметь Flux of DataBuffer или что-то еще? И если да, то как клиент должен вести себя и доставлять данные в таком формате, что это действительно потоковый подход?

Затем класс FileService должен отправить эти файлы в чужую систему, возможно, сделать что-то еще с заданными данными, по крайней мере, зарегистрировать идентификатор и имя файла. :-) Наш код в этом FileService.save (..) на самом деле выглядит следующим образом:

...
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
    bodyBuilder.asyncPart(...take mono somehow...);
    bodyBuilder.part("id", id);
    return webClient.create("url-of-foreign-system")
            .uri("/uploadFile")
            .syncBody(bodyBuilder.build())
            .retrieve()
            .bodyToMono(Result.class);
...

К сожалению, вторая конечная точка REST, наша сторонняя система, выглядит немного иначе, чем наша первый. Он будет обогащен данными из другой системы. Он передает некоторый FileDO2 с идентификатором и байтовым массивом, а также некоторые другие метаданные, указывающие c для второй сторонней системы.

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

Есть идеи сделать это в целом? В настоящее время мы не имеем ни малейшего понятия, чтобы сделать это все ... Мы ценим любую помощь или идеи.

...