Краткое руководство по использованию нескольких учетных записей Azure Storage из одного приложения Spring Boot

Проекты Spring в целом имеют свое мнение: 80-90% случаев использования обрабатываются «по умолчанию», а код часто намного лаконичнее, чем требовалось бы в противном случае, из-за того, что Spring предпочитает конвенции конфигурации. Эти и другие «мнения» могут привести к значительно меньшему объему кода для написания и поддержки и, как следствие, к более целенаправленному воздействию.

В подавляющем большинстве случаев, когда Azure Storage используется из приложения, нет убедительных преимуществ использования более чем одной учетной записи Azure Storage. Но есть граничные случаи, и возможность использовать несколько учетных записей Azure Storage из одного приложения — даже если такая возможность может понадобиться только в 10% случаев — может стать невероятно полезным расширением наших суперспособностей в области хранения данных.

Эта статья является результатом сотрудничества с Ши ли Ченом.

Все дело в ресурсах

Spring Framework определяет интерфейс Resource и предоставляет несколько реализаций, построенных на Resource, чтобы облегчить разработчикам доступ к низкоуровневым ресурсам. Для того чтобы работать с определенным видом ресурсов, необходимы две вещи:

  • реализация Resource.
  • реализация ResourcePatternResolver.

Приложение Spring оценивает ресурсы, используя один или несколько зарегистрированных резольверов. Когда тип ресурса определен, соответствующая реализация Resource используется для доступа и/или манипулирования базовым ресурсом.

Если реализации, встроенные в Spring Framework, не удовлетворяют вашему сценарию использования, довольно просто добавить поддержку дополнительных типов ресурсов, определив собственные реализации интерфейсов AbstractResource и ResourcePatternResolver.

В этой статье мы представим Spring Resource, рассмотрим реализацию Spring Cloud Azure Resource (особенно в отношении соображений и ограничений Azure Storage Account), а также рассмотрим, как расширить эту реализацию для решения тех частных случаев, когда было бы полезно получить доступ к нескольким Azure Storage Accounts из одного приложения Spring Boot.

Становимся изобретательными

Мы уже упоминали, что Spring Framework определяет несколько полезных реализаций ресурсов. На момент написания этой статьи они используются по умолчанию:

  • UrlResource
  • ClassPathResource
  • FileSystemResource
  • PathResource
  • ServletContextResource
  • InputStreamResource
  • ByteArrayResource

Как упоминалось ранее, каждый ресурс будет иметь соответствующий резольвер ресурсов.

Для того чтобы ваше приложение Spring Boot могло использовать пользовательский Resource, необходимо выполнить следующие действия:

  • Реализовать интерфейс Resource путем расширения AbstractResource.
  • Реализовать интерфейс ResourcePatternResolver для разрешения пользовательского типа ресурса
  • Зарегистрируйте реализацию ResourcePatternResolver как боб.

ПРИМЕЧАНИЕ: Ваш резольвер должен быть добавлен в набор резольверов загрузчика ресурсов по умолчанию с помощью метода org.springframework.core.io.DefaultResourceLoader#addProtocolResolver, но этот код присутствует в AbstractAzureStorageProtocolResolver; расширение этого класса для создания вашей реализации выполнит это от вашего имени, если вы не решите переопределить его метод setResourceLoader.

ResourceLoader пытается разрешить каждый Resource, сравнивая его определенное местоположение/формат со всеми зарегистрированными преобразователями шаблонов протокола, пока не будет возвращен ненулевой ресурс. Если совпадение не найдено, Resource будет оценен с помощью встроенных в Spring шаблонизаторов.

Ресурсы Spring в Spring Cloud Azure

Spring Cloud Azure предоставляет две реализации Spring resource и resource pattern resolver. В этой статье мы рассмотрим только реализацию ресурса Azure Storage Blob. Вы можете изучить исходный код Spring Cloud Azure Resources на сайте Spring Cloud Azure и соответствующую документацию на сайте Resource Handling.

ПРИМЕЧАНИЕ: Для анализа и экспериментов мы используем Spring Cloud Azure Starter Storage Blob версии 4.2.0.

Реализация AbstractResource

Абстрактная реализация AzureStorageResource для Spring Cloud Azure в первую очередь определяет формат протокола ресурса хранения Azure и учитывает уникальные атрибуты службы Azure Storage Account, например, имя контейнера и имя файла. Важно отметить, что AzureStorageResource отделен от Azure Storage SDK.

Интерфейс Spring Framework WritableResource представляет базовый API, на котором мы строим чтение из ресурса Azure Storage и запись в него.

abstract class AzureStorageResource extends AbstractResource implements WritableResource {

    private boolean isAzureStorageResource(@NonNull String location) {
        ......
    }

    String getContainerName(String location) {
        ......
    }

    String getContentType(String location) {
        ......
    }

    String getFilename(String location) {
        ......
    }

    abstract StorageType getStorageType();
}
Вход в полноэкранный режим Выход из полноэкранного режима

Ресурс StorageBlobResource является реализацией Spring Cloud Azure Storage Blob абстрактного класса AbstractResource.
Мы видим, что StorageBlobResource использует BlobServiceClient из Azure Storage Blob SDK для реализации всех абстрактных методов, полагаясь на клиент службы для взаимодействия со службой Azure Storage Blob.

public final class StorageBlobResource extends AzureStorageResource {
    private final BlobServiceClient blobServiceClient;
    private final BlobContainerClient blobContainerClient;
    private final BlockBlobClient blockBlobClient;

    public StorageBlobResource(BlobServiceClient blobServiceClient, String location, Boolean autoCreateFiles,
                               String snapshot, String versionId, String contentType) {
        ......
        this.blobContainerClient = blobServiceClient.getBlobContainerClient(getContainerName(location));
        BlobClient blobClient = blobContainerClient.getBlobClient(getFilename(location));
        this.blockBlobClient = blobClient.getBlockBlobClient();
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        try {
            ......
            return this.blockBlobClient.getBlobOutputStream(options);
        } catch (BlobStorageException e) {
            throw new IOException(MSG_FAIL_OPEN_OUTPUT, e);
        }
    }

    ......

    @Override
    StorageType getStorageType() {
        return StorageType.BLOB;
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Реализация ResourcePatternResolver

Spring Cloud Azure предоставляет абстрактную реализацию AbstractAzureStorageProtocolResolver. Этот класс включает общую обработку протокола ресурсов хранения Azure, раскрывает специфические возможности службы Azure Storage Account и добавляет необходимую логику в загрузчик ресурсов по умолчанию. Как и AzureStorageResource, AbstractAzureStorageProtocolResolver также не связан с Azure Storage SDK.

public abstract class AbstractAzureStorageProtocolResolver implements ProtocolResolver, ResourcePatternResolver,
    ResourceLoaderAware, BeanFactoryPostProcessor {

    protected final AntPathMatcher matcher = new AntPathMatcher();

    protected abstract StorageType getStorageType();

    protected abstract Resource getStorageResource(String location, Boolean autoCreate);

    protected ConfigurableListableBeanFactory beanFactory;

    protected abstract Stream<StorageContainerItem> listStorageContainers(String containerPrefix);

    protected abstract StorageContainerClient getStorageContainerClient(String name);

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        if (resourceLoader instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) resourceLoader).addProtocolResolver(this);
        } else {
            LOGGER.warn("Custom Protocol using azure-{}:// prefix will not be enabled.", getStorageType().getType());
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Resource resolve(String location, ResourceLoader resourceLoader) {
        if (AzureStorageUtils.isAzureStorageResource(location, getStorageType())) {
            return getResource(location);
        }
        return null;
    }

    @Override
    public Resource[] getResources(String pattern) throws IOException {
        Resource[] resources = null;

        if (AzureStorageUtils.isAzureStorageResource(pattern, getStorageType())) {
            if (matcher.isPattern(AzureStorageUtils.stripProtocol(pattern, getStorageType()))) {
                String containerPattern = AzureStorageUtils.getContainerName(pattern, getStorageType());
                String filePattern = AzureStorageUtils.getFilename(pattern, getStorageType());
                resources = resolveResources(containerPattern, filePattern);
            } else {
                return new Resource[] { getResource(pattern) };
            }
        }
        if (null == resources) {
            throw new IOException("Resources not found at " + pattern);
        }
        return resources;
    }

    @Override
    public Resource getResource(String location) {
        Resource resource = null;

        if (AzureStorageUtils.isAzureStorageResource(location, getStorageType())) {
            resource = getStorageResource(location, true);
        }

        if (null == resource) {
            throw new IllegalArgumentException("Resource not found at " + location);
        }
        return resource;
    }

    /**
     * Storage container item.
     */
    protected static class StorageContainerItem {
        private final String name;
        ......
    }

    protected static class StorageItem {

        private final String container;
        private final String name;
        private final StorageType storageType;
        ......
    }

    protected interface StorageContainerClient {

        ......
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Резольвер ресурсов AzureStorageBlobProtocolResolver является реализацией Spring Cloud Azure Storage Blob ResourcePatternResolver. Он инкапсулирует ресурсы в соответствии с шаблоном расположения или элемента хранения на основе BlobServiceClient и возвращает связанный StorageBlobResource.

public final class AzureStorageBlobProtocolResolver extends AbstractAzureStorageProtocolResolver {

    private BlobServiceClient blobServiceClient;
    @Override
    protected StorageType getStorageType() {
        return StorageType.BLOB;
    }

    @Override
    protected Resource getStorageResource(String location, Boolean autoCreate) {
        return new StorageBlobResource(getBlobServiceClient(), location, autoCreate);
    }

    private BlobServiceClient getBlobServiceClient() {
        if (blobServiceClient == null) {
            blobServiceClient = beanFactory.getBean(BlobServiceClient.class);
        }
        return blobServiceClient;
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Мнения

Как уже упоминалось в начале этой заметки, возможности по умолчанию прекрасно выполняют требования в подавляющем большинстве случаев. Но в соответствии с принципами Spring, Spring Cloud Azure Starter Storage Blob был разработан для беспроблемного решения 80-90% случаев использования «из коробки», в то время как остальные (граничные) случаи могут быть реализованы с некоторыми дополнительными усилиями.

Как написано, ресурс Storage Blob поддерживает несколько операций с контейнерами, используя один и тот же аккаунт хранения. Важным моментом является то, что пути к блобам в разных контейнерах могут быть правильно разрешены в объекты StorageBlobResource. Комбинируя предыдущий код для StorageBlobResource, ресурс blob должен содержать клиента службы blob, и если blobServiceClient.getBlobContainerClient(getContainerName(location)) успешно возвращает BlobServiceClient, ресурс blob может быть разрешен и получен.

Боб BlobServiceClient представляет учетную запись Azure Storage Account в Azure Storage Blob SDK, что означает, что текущая реализация не поддерживает одновременную доступность с использованием нескольких Azure Storage Accounts.

Разработка расширенной версии Spring Cloud Azure Starter Storage Blob

Для тех редких случаев, когда может быть полезным одновременный доступ к нескольким учетным записям Azure Storage из одного приложения, существует способ сделать это. Чтобы продемонстрировать эту возможность, давайте создадим новую библиотеку под названием spring-cloud-azure-starter-storage-blob-extend. Единственной внешней зависимостью для этой новой библиотеки является существующая spring-cloud-azure-starter-storage-blob.

Расширение свойств Storage Blob

Хотя основной целью является поддержка нескольких учетных записей хранилищ, вторичной целью проектирования является использование структуры, аналогичной AzureStorageBlobProperties, чтобы минимизировать кривую обучения и сохранить функции аутентификации Spring Cloud Azure 4.0.

public class ExtendAzureStorageBlobsProperties {

    public static final String PREFIX = "spring.cloud.azure.storage.blobs";

    private boolean enabled = true;

    private final List<AzureStorageBlobProperties> configurations = new ArrayList<>();

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public List<AzureStorageBlobProperties> getConfigurations() {
        return configurations;
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Динамическая регистрация бобов Storage Blob

Поскольку будет несколько конфигураций Storage Account, мы должны назвать бобы, соответствующие каждому Storage Account. Самый чистый подход — просто использовать имя учетной записи в качестве имени боба.

Теперь давайте динамически зарегистрируем эти бобы в контексте Spring.

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = { "spring.cloud.azure.storage.blobs.enabled"}, havingValue = "true")
public class ExtendStorageBlobsAutoConfiguration implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

    private Environment environment;

    public static final String EXTEND_STORAGE_BLOB_PROPERTIES_BEAN_NAME = "extendAzureStorageBlobsProperties";

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        AzureGlobalProperties azureGlobalProperties =
            Binder.get(environment)
                  .bind(AzureGlobalProperties.PREFIX, AzureGlobalProperties.class)
                  .orElse(new AzureGlobalProperties());
        ExtendAzureStorageBlobsProperties blobsProperties =
            Binder.get(environment)
                  .bind(ExtendAzureStorageBlobsProperties.PREFIX, ExtendAzureStorageBlobsProperties.class)
                  .orElseThrow(() -> new IllegalArgumentException("Can not bind the azure storage blobs properties."));
        // merge properties
        for (AzureStorageBlobProperties azureStorageBlobProperties : blobsProperties.getConfigurations()) {
            AzureStorageBlobProperties transProperties = new AzureStorageBlobProperties();
            AzureGlobalPropertiesUtils.loadProperties(azureGlobalProperties, transProperties);
            copyAzureCommonPropertiesIgnoreTargetNull(transProperties, azureStorageBlobProperties);
        }

        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
        registryBeanExtendAzureStorageBlobsProperties(factory, blobsProperties);
        blobsProperties.getConfigurations().forEach(blobProperties -> registryBlobBeans(factory, blobProperties));
    }

    private void registryBeanExtendAzureStorageBlobsProperties(DefaultListableBeanFactory beanFactory,
                                                               ExtendAzureStorageBlobsProperties blobsProperties) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExtendAzureStorageBlobsProperties.class,
            () -> blobsProperties);
        AbstractBeanDefinition rawBeanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        beanFactory.registerBeanDefinition(EXTEND_STORAGE_BLOB_PROPERTIES_BEAN_NAME, rawBeanDefinition);
    }

    private void registryBlobBeans(DefaultListableBeanFactory beanFactory, AzureStorageBlobProperties blobProperties) {
        String accountName = getStorageAccountName(blobProperties);
        Assert.hasText(accountName, "accountName can not be null or empty.");
        registryBeanStaticConnectionStringProvider(beanFactory, blobProperties, accountName);
        registryBeanBlobServiceClientBuilderFactory(beanFactory, blobProperties, accountName);
        registryBeanBlobServiceClientBuilder(beanFactory, accountName);
        registryBeanBlobServiceClient(beanFactory, accountName);
        registryBeanBlobContainerClient(beanFactory, blobProperties, accountName);
        registryBeanBlobClient(beanFactory, blobProperties, accountName);
    }

    private void registryBeanBlobServiceClientBuilder(DefaultListableBeanFactory beanFactory,
                                                      String accountName) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(BlobServiceClientBuilder.class,
            () -> {
                BlobServiceClientBuilderFactory builderFactory =
                    beanFactory.getBean(accountName + BlobServiceClientBuilderFactory.class.getSimpleName(),
                        BlobServiceClientBuilderFactory.class);
                return builderFactory.build();
            });
        AbstractBeanDefinition rawBeanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        beanFactory.registerBeanDefinition(
            accountName + BlobServiceClientBuilder.class.getSimpleName(), rawBeanDefinition);
    }

    ......

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Расширение AzureStorageBlobProtocolResolver

Следующая задача заключается в том, чтобы сделать любой контейнер разрешаемым одним и тем же resolver’ом шаблонов ресурсов. Указав местоположение ресурса хранения блобов, например azure-blob-accountname://containername/test.txt, резолвер будет использовать его для поиска соответствующего боба BlobServiceClient по имени Azure Storage Account и вернет ресурс хранения.

public class ExtendAzureStorageBlobProtocolResolver extends ExtendAbstractAzureStorageProtocolResolver {
    private final Map<String, BlobServiceClient> blobServiceClientMap = new HashMap<>();

    @Override
    protected Resource getStorageResource(String location, Boolean autoCreate) {
        return new ExtendStorageBlobResource(getBlobServiceClient(location), location, autoCreate);
    }

    private BlobServiceClient getBlobServiceClient(String locationPrefix) {
        String storageAccount = ExtendAzureStorageUtils.getStorageAccountName(locationPrefix, getStorageType());
        Assert.notNull(storageAccount, "storageAccount can not be null.");
        String accountKey = storageAccount.toLowerCase(Locale.ROOT);
        if (blobServiceClientMap.containsKey(accountKey)) {
            return blobServiceClientMap.get(accountKey);
        }

        BlobServiceClient blobServiceClient = beanFactory.getBean(
            accountKey + BlobServiceClient.class.getSimpleName(), BlobServiceClient.class);
        Assert.notNull(blobServiceClient, "blobServiceClient can not be null.");
        blobServiceClientMap.put(accountKey, blobServiceClient);
        return blobServiceClient;
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Снова необходимо добавить боб ExtendAzureStorageBlobProtocolResolver в контекст Spring.

Тестирование Spring Cloud Azure Starter Storage Blob Extend

Вы можете использовать start.spring.io для создания проекта Spring Boot 2.6.7 или более поздней версии с поддержкой Azure Storage (или построить на основе этого примера блоба хранения, если вам так удобнее).

Добавьте зависимость extending starter в файл pom.xml:

<dependency>
  <groupId>com.azure.spring.extend</groupId>
  <artifactId>spring-cloud-azure-starter-storage-blob-extend</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>
Войдите в полноэкранный режим Выйти из полноэкранного режима

Удалите файл src/main/resources/application.properties или добавьте следующий конфигурационный файл application-extend.yml, который позволяет использовать несколько учетных записей хранилища:

application-extend.yml

spring:
  cloud:
    azure:
      storage:
        blob:
          enabled: false
        blobs:
          enabled: true
          configurations:
            - account-name: ${FIRST_ACCOUNT}
              container-name: ${FIRST_CONTAINER}
              account-key: ${ACCOUNT_KEY_OF_FIRST_ACCOUNT}
            - account-name: ${SECOND_ACCOUNT}
              container-name: ${SECOND_CONTAINER}
              account-key: ${ACCOUNT_KEY_OF_SECOND_ACCOUNT}
Вход в полноэкранный режим Выход из полноэкранного режима

ПРИМЕЧАНИЕ: Вы должны предоставить значения для вышеуказанных переменных среды (перечисленных заглавными буквами) с активной информацией о ресурсе Azure Storage Account.

Добавьте класс com.azure.spring.extend.sample.storage.resource.extend.SampleDataInitializer со следующим телом:

@Profile("extend")
@Component
public class SampleDataInitializer implements CommandLineRunner {
    final static Logger logger = LoggerFactory.getLogger(SampleDataInitializer.class);

    private final ConfigurableEnvironment env;

    private final ExtendAzureStorageBlobProtocolResolver resolver;
    private final ExtendAzureStorageBlobsProperties properties;

    public SampleDataInitializer(ConfigurableEnvironment env, ExtendAzureStorageBlobProtocolResolver resolver,
                                 ExtendAzureStorageBlobsProperties properties) {
        this.env = env;
        this.resolver = resolver;
        this.properties = properties;
    }

    /**
     * This is used to initialize some data for each Azure Storage Account Blob container.
     */
    @Override
    public void run(String... args) {
        properties.getConfigurations().forEach(this::writeDataByStorageAccount);
    }

    private void writeDataByStorageAccount(AzureStorageBlobProperties blobProperties) {
        String containerName = blobProperties.getContainerName();
        if (!StringUtils.hasText(containerName) || blobProperties.getAccountName() == null) {
            return;
        }

        String accountName = getStorageAccountName(blobProperties);
        logger.info("Begin to initialize the {} container of the {} account", containerName, accountName);
        long currentTimeMillis = System.currentTimeMillis();
        String fileName = "fileName-" + currentTimeMillis;
        String data = "data" + currentTimeMillis;
        Resource storageBlobResource = resolver.getResource("azure-blob-" + accountName + "://" + containerName +"/" + fileName + ".txt");
        try (OutputStream os = ((WritableResource) storageBlobResource).getOutputStream()) {
            os.write(data.getBytes());
            logger.info("Write data to container={}, fileName={}.txt", containerName, fileName);
        } catch (IOException e) {
            logger.error("Write data exception", e);
        }
        logger.info("End to initialize the {} container of the {} account", containerName, accountName);
    }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Запустите образец с помощью следующей команды Maven:

mvn clean spring-boot:run -Dspring-boot.run.profiles=extend

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

c.a.s.e.s.s.r.e.SampleDataInitializer    : Begin to initialize the container first of the account firstaccount.
c.a.s.e.s.s.r.e.SampleDataInitializer    : Write data to container=first, fileName=fileName-1656641340271.txt
c.a.s.e.s.s.r.e.SampleDataInitializer    : End to initialize the container first of the account firstaccount.
c.a.s.e.s.s.r.e.SampleDataInitializer    : Begin to initialize the container second of the account secondaccount.
c.a.s.e.s.s.r.e.SampleDataInitializer    : Write data to container=second, fileName=fileName-1656641343572.txt
c.a.s.e.s.s.r.e.SampleDataInitializer    : End to initialize the container second of the account secondaccount.
Вход в полноэкранный режим Выход из полноэкранного режима

Весь код примера проекта опубликован в репозитории spring-cloud-azure-starter-storage-blob-extend-sample.

В этом расширенном приложении все еще можно вернуться к первоначальному использованию Spring Cloud Azure Starter Storage Blob с одной учетной записью хранения, добавив следующий файл конфигурации application-current.yml:

spring:
  cloud:
    azure:
      storage:
        blob:
          account-name: ${FIRST_ACCOUNT}
          container-name: ${FIRST_CONTAINER}
          account-key: ${ACCOUNT_KEY_OF_FIRST_ACCOUNT}
current:
  second-container: ${SECOND_CONTAINER}
Войти в полноэкранный режим Выйти из полноэкранного режима

ПРИМЕЧАНИЕ: Вы должны установить или заменить перечисленные значения, присвоенные переменным окружения, информацией об активном ресурсе Azure Storage Account.

Запустите образец с помощью следующей команды Maven:

mvn clean spring-boot:run -Dspring-boot.run.profiles=current

Чтобы убедиться в правильности работы с использованием одной учетной записи хранилища, сравните вывод терминала с приведенным здесь:

c.a.s.e.s.s.r.c.SampleDataInitializer    : StorageApplication data initialization of 'first-container' begin ...
c.a.s.e.s.s.r.c.SampleDataInitializer    : Write data to container=first-container, fileName=fileName1656641162614.txt
c.a.s.e.s.s.r.c.SampleDataInitializer    : StorageApplication data initialization of 'first-container' end ...
c.a.s.e.s.s.r.c.SampleDataInitializer    : StorageApplication data initialization of 'second-container' begin ...
c.a.s.e.s.s.r.c.SampleDataInitializer    : Write data to container=second-container, fileName=fileName1656641165411.txt
c.a.s.e.s.s.r.c.SampleDataInitializer    : StorageApplication data initialization of 'second-container' end ...
Вход в полноэкранный режим Выход из полноэкранного режима

Заключение

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

Один момент, который заслуживает внимания, это определение протокола для ресурса, например, Azure Storage Blob Resource. Мы должны отметить, используем ли мы azure-blob:// или azure-blob-[account-name]://, и спланировать возможности приложения соответствующим образом. Кроме того, поскольку идентификатор сетевого ресурса должен быть однозначно идентифицируемым, последний формат расположения может привести к гораздо более длинному имени и также раскрывает имя учетной записи хранилища. Эти компромиссы должны быть оценены в свете требований и профиля риска.

Ссылки и полезные ресурсы

  • Последняя документация по ресурсам Spring
  • Расширенный стартер для Spring Cloud Azure Starter Storage Blob и пример на GitHub

Оцените статью
devanswers.ru
Добавить комментарий