Рубрики
Без рубрики

Использование нового пакета SDK Azure для Java для асинхронной загрузки изображений с помощью Spring Reactor

В этом сообщении показано, как использовать новый пакет SDK Azure для хранилища больших двоичных объектов с использованием асинхронного API для повышения производительности и стабильности. С тегами spring, azure, java, реактивный.

Новый пакет SDK Azure для Java

В этом сообщении в блоге используется предстоящий пакет SDK Azure для Java: на момент написания этой статьи это все еще предварительный выпуск, но многие люди уже начали его использовать.

Чтобы быть более точным, мы говорим здесь о Azure SDK для Java (предварительный просмотр в августе 2019 года) , также рассматриваемом как “1.0.0-предварительный просмотр.2” в Maven Central.

Этот новый выпуск важен, поскольку он использует некоторые новые, современные Рекомендации API , а также потому, что его асинхронные функции работают на Spring Reactor .

Эти новые реактивные API интересны, поскольку они обеспечивают лучшую масштабируемость и очень хорошо работают с Spring. К сожалению, они возникают за счет использования API, который сложнее понять и сложнее отлаживать: вот почему мы делаем этот пост в блоге!

Проблема с загрузкой данных

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

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

Пружинный реактор и поток пружинного полотна

Пожалуйста, обратите внимание, что приведенный здесь пример работает с Spring Webflux: это реактивная версия Spring, и поскольку она работает совершенно иначе, чем классический Spring MVC (основанный на потоках), этот код не будет работать с Spring MVC. Это одна из проблем при выполнении реактивного программирования: на самом деле невозможно смешивать реактивный код и код на основе потоков, поэтому весь ваш проект нужно будет сделать и продумать с помощью реактивного API. На мой взгляд, это лучше всего подходит для архитектуры микросервисов, где вы будете кодировать некоторые конкретные сервисы с помощью реактивного API, а некоторые – с помощью традиционного API на основе потоков. Реактивные микросервисы, вероятно, будут немного сложнее разрабатывать и отлаживать, но обеспечат лучшую масштабируемость, время запуска, потребление памяти, поэтому они будут использоваться для некоторых конкретных, ресурсоемких задач.

Создание учетной записи хранения

На портале Azure создайте новую учетную запись хранения:

Как только он будет создан, перейдите к этой учетной записи хранения и выберите “Подпись общего доступа” или SAS. SAS – это подпись, которая позволяет получить доступ к некоторым ресурсам в течение ограниченного периода времени: она идеально подходит для загрузки данных в определенном месте без ущерба для безопасности.

После нажатия кнопки “Создать SAS и строку подключения” скопируйте последнее сгенерированное текстовое поле с именем “URL SAS службы BLOB-объектов”. Это тот, который мы будем использовать с пакетом SDK Azure для Java.

Добавьте пакет SDK Azure для Java в pom.xml

Поскольку Azure SDK для предварительного просмотра Java находится в Maven Central, это всего лишь вопрос добавления зависимости в проект pom.xml:


    com.azure
    azure-storage-blob
    12.0.0-preview.2


Использование нового асинхронного API

Давайте сначала взглянем на окончательный код, который доступен на https://github.com/jdubois/jhipster-azure-blob-storage/blob/master/src/main/java/com/example/demo/PictureResource.java :

@RestController
@RequestMapping("/api")
public class PictureResource {

    Logger log = LoggerFactory.getLogger(PictureResource.class);

    @Value("${AZURE_BLOB_ENDPOINT}")
    private String endpoint;

    @PostMapping("/picture")
    public void uploadPicture() throws IOException {
        log.debug("Configuring storage client");
        BlobServiceAsyncClient client =  new BlobServiceClientBuilder()
            .endpoint(endpoint)
            .buildAsyncClient();

        client.createContainer("pictures")
            .doOnError((e) -> {
                log.info("Container already exists");
            })
            .flatMap(
                (clientResponse) -> {
                    log.info("Uploading picture");
                    return clientResponse
                        .value()
                        .getBlockBlobAsyncClient("picture.png")
                        .uploadFromFile("src/main/resources/image.png");
                })
            .subscribe();
    }
}

ПРЕДУПРЕЖДЕНИЕ Этот API работает только при использовании Spring Reactive, поэтому, пожалуйста, обратите внимание, что вам нужно использовать его в проекте Spring Webflux, а не в проекте Spring MVC.

Аутентификация выполняется с помощью “URL-адреса SAS службы Blob-объектов”, который мы скопировали выше, и который предоставляется с использованием переменной среды AZURE_BLOB_ENDPOINT в этом примере: обратите внимание, что SAS включен в URL-адрес, поэтому нет необходимости проходить аутентификацию в другом месте (в API есть метод credentials() , который может вводить в заблуждение, но в нашем случае бесполезен). Таким образом, этот URL-адрес должен храниться надежно и не фиксироваться с помощью вашего кода.

При отправке изображения используется API Spring Reactor:

  • Мы создаем определенный картинки контейнер для хранения некоторых данных
  • Затем мы используем API Spring Reactor для загрузки изображения
  • И мы заканчиваем с помощью метода subscribe() , который заставляет наш код выполняться асинхронно

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

Улучшение реактивного кода

Этот совет был предоставлен Кристофером Барнеттом , огромное ему спасибо!

Предыдущий код – это то, что мы обычно видим в проектах, но его можно улучшить, чтобы позволить Spring Webflux обрабатывать .subscribe () ()

Изменение можно увидеть в этой фиксации , где мы заменяем .subscribe() на .затем() и мы возвращаем Mono вместо того, чтобы ничего не возвращать. Ответственность Spring Webflux будет заключаться в том, чтобы справиться с этим Mono и позвонить .подписаться () .

Результирующий код выглядит следующим образом:

    @PostMapping("/picture")
    public Mono uploadPicture() throws IOException {
        log.debug("Configuring storage client");
        BlobServiceAsyncClient client =  new BlobServiceClientBuilder()
            .endpoint(endpoint)
            .buildAsyncClient();

        return client.createContainer("pictures")
            .doOnError((e) -> {
                log.info("Container already exists");
            })
            .flatMap(
                (clientResponse) -> {
                    log.info("Uploading picture");
                    return clientResponse
                        .value()
                        .getBlockBlobAsyncClient("picture.png")
                        .uploadFromFile("src/main/resources/image.png");
                })
            .then();
    }

Это немного более продвинутое использование реактивных API, но результат должен стоить потраченных усилий.

Заключение и обратная связь

В настоящее время нам не хватает документации и образцов этого нового асинхронного API в Azure SDK для Java. Я считаю, что это очень важно в некоторых конкретных сценариях, подобных тому, который мы имеем здесь, так как обычно вы не должны загружать или скачивать данные в текущем потоке, если вам нужно масштабируемое приложение.

Этот SDK все еще находится в предварительном просмотре, поэтому, если у вас есть отзывы об этом API, пожалуйста, прокомментируйте этот пост!

Например, текущий API позволяет вам создать контейнер (и это завершится неудачей, если контейнер уже существует) или получить существующий контейнер (и это завершится неудачей, если он еще не существует). Как вы думаете, должна ли быть опция, чтобы иметь что-то вроде getOrCreateContainer("имя") , которая автоматически создаст контейнер, если он будет запрошен?

Оригинал: “https://dev.to/azure/using-the-new-azure-sdk-for-java-to-upload-images-asynchronously-using-spring-reactor-20ha”