Архитектура микросервиса соответствует Приложению Двенадцати Факторов (12factor.net). В соответствии с принципом 12 факторов рекомендуется, чтобы конфигурации приложения были отделены от исполняемого приложения. Внешние конфигурации не только предоставляют исполняемые файлы, не зависящие от среды, но также обеспечивают простой и безопасный способ управления конфигурациями во время выполнения без увеличения времени простоя.

Microsoft Azure предоставляет аналогичную функциональность через службу конфигурации приложений, которую можно легко интегрировать с приложением Spring Boot. С помощью этой службы конфигурации приложений разработчики могут экспортировать все настраиваемые параметры своего приложения в приложение Azure.

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

  1. Действия, необходимые для конфигурации приложений Azure.
  2. Требуемые конфигурации в приложении микросервиса.
  3. Варианты использования для доступа к параметрам в компоненте приложения.

1. Действия, необходимые для настройки приложения Azure

Первый шаг — создать ресурс «Конфигурация приложения» на веб-портале Azure. После этого можно добавить все настраиваемые параметры в эту App-Configuration.

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

Пожалуйста, выполните следующие шаги для настройки, упомянутой выше.

Шаг 1. Создайте учетную запись в Azure DevOps. Создайте ресурс конфигурации приложения.

Шаг 2. Добавьте новые конфигурации в ресурс конфигурации приложения.

Шаг 3. Добавьте новый флаг функции в ресурс конфигурации приложения.

Шаг 4. Перейдите в раздел «Ключи доступа» и скопируйте значение строки подключения, которое будет использоваться в конфигурации Spring Boot.

Предупреждение. Приведенные выше экраны соответствуют текущему пользовательскому интерфейсу портала Azure. (Пожалуйста, следуйте ресурсу Azure для получения последних изменений здесь: https://learn.microsoft.com/en-us/azure/azure-app-configuration/)

2. Требуемые конфигурации в микросервисном приложении

После настройки всех настраиваемых параметров в облаке Azure вторым шагом является настройка приложения Spring Boot для подключения к приложению Azure и получение конфигураций при запуске сервера.

Обратите внимание, что параметры подключения и необходимые конфигурации ключей должны быть добавлены в bootstrap.yml.

Давайте пройдемся по шагам ниже:

Шаг 1:добавьте следующие зависимости в build.gradle.

dependencies {
  implementation 'com.azure.spring:spring-cloud-azure-appconfiguration-config-web:5.2.0'
  implementation 'com.azure.spring:spring-cloud-azure-feature-management-web:5.2.0'
  implementation 'com.azure:azure-sdk-bom:1.2.13'
  implementation 'com.azure:azure-data-appconfiguration:1.4.5'
}

Шаг 2. Добавьте сведения о подключении и конфигурации в bootstrap.yml.

  • connection-string: вы бы скопировали с портала, как указано в предыдущих шагах.
  • key-filter:ключевой фильтр — это ключ, который вы присвоили своему настраиваемому параметру. (например, «screenConfigurations» — это ключ, используемый в приведенном выше примере), значения с этими ключами будут извлечены из облака.
  • label-filter: чтобы разделить одно и то же свойство для разных сред.
spring:
  cloud:
    azure:
      appconfiguration:
        stores:
          - connection-string: Endpoint=https://xyz.azconfig.io;Id=****;Secret=*************
            selects:
              - key-filter: screenConfigurations
                label-filter: dev
              - key-filter: filterKey
                label-filter: dev
            feature-flags:
              enabled: true
              label-filter: dev

Шаг 3.Это необязательная конфигурация. Это требуется только тогда, когда вы хотите получить доступ к настраиваемым параметрам через аннотацию @ConfigurationProperties.

Аннотируйте пилотный класс вашего приложения Spring Boot с помощью аннотаций @ConfigurationPropertiesScan и @EnableConfigurationProperties.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@ConfigurationPropertiesScan
@EnableConfigurationProperties
@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        SpringApplication.run(AppConfig.class, args);
    }
}

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

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

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

Вариант использования 1.Доступ к значениям конфигурации с помощью аннотации @Value и класса @ConfigurationProperties

Учтите, что у вас разные экраны в ваших приложениях. Каждый экран имеет два свойства: «включен» и «размерен». И вы настроили его значения как JSON в конфигурации приложения Azure, используя ключ «screenConfigurations» (упомянутый выше вbootstrap.yml), и его значение JSON выглядит следующим образом:

{
  "screens": [
    {
      "gymRegistration": {
        "enabled": true,
        "sizeable": false
      }
    },
    {
      "libraryRegistration": {
        "enabled": true,
        "sizeable": false
      }
    }
  ]
}

Приложение будет получать вышеуказанные свойства при запуске сервера и сопоставлять с классом «ScreenProperties.java», который помечен @ConfigurationProperties, как показано ниже:

import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;

@ConfigurationProperties
@Data
public class ScreenProperties {
  private Map<String, Map<String, Screen>> screens;
  public static record Screen (boolean enabled, boolean sizeable){
  }
}

На всякий случай, чтобы получить доступ к отдельному свойству непосредственно в компоненте, можно использовать аннотацию @value с именем ключа, как указано ниже.

import java.net.MalformedURLException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(Constant.RESOURCE_PATH_PROPERTY)
public class PropertyController {
  
  @Value("${.screens[0].gymRegistration.enabled}")
  private String isGymRegistrationEnabled;
  
  private ScreenProperties screenproperties;
    
  public PropertyController(ScreenProperties screenProperties) {
    this.screenProperties = screenProperties;
  }

  @GetMapping(value= {"printProperties"})
  public String printProperties() throws MalformedURLException {
     System.out.println("Single property via @Value annotation : "+ isGymRegistrationEnabled);
     System.out.println("All properties via @ConfigurationProperties annotation : "+ screenProperties.toString());  
  }
}

Пример использования 2. Включение/отключение функции с помощью флага функции.

Флаг функции используется для включения или отключения определенных функций путем устранения стандартного кодирования.

Его значение извлекается из cloud-config каждые 30 секунд (значение по умолчанию, которое можно изменить), чтобы его можно было переключать во время выполнения без остановки нашего приложения.

В приведенном ниже примере я переключаю возвращаемое значение (которое находится в форме строки) метода на основе значения флага функции.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.azure.spring.cloud.feature.management.FeatureManager;

@RestController
@RequestMapping(Constant.RESOURCE_PATH_FEATUE)
public class FeatureController {

  private FeatureManager featureManager;

  public FeatureController(FeatureManager featureManager) {
    this.featureManager = featureManager; 
  }

  @Value("${cloud.config.feature.testFeatureFlag}")
  private String testFeatureFlag;

  @GetMapping(value= {"/message/","/"})
  public String getFeatureMessage() {

    featureManager.getAllFeatureNames().forEach(System.out::println);

    return (featureManager.getAllFeatureNames().contains(biometricFeatureName)) 
        ? (featureManager.isEnabled(biometricFeatureName)) ? "Hey, feature flag is enabled" : "Ooops, feature flag is disabled"
          : "Ooops, feature flag is not available in the system" ;
  } 
}

Вариант использования 3: операции CRUD для конфигураций через Spring Boot

Чтобы выполнить операцию CRUD для настраиваемых параметров, Azure предоставляет пакеты SDK.

Мы можем использовать «ConfigurationClient.class» для выполнения различных операций CRUD, как указано ниже:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.azure.core.http.rest.PagedIterable;
import com.azure.data.appconfiguration.ConfigurationClient;
import com.azure.data.appconfiguration.ConfigurationClientBuilder;
import com.azure.data.appconfiguration.models.ConfigurationSetting;
import com.azure.data.appconfiguration.models.SettingSelector;

@Service  
public class ConfigServiceImpl implements ConfigService{

  @Value("${spring.cloud.azure.appconfiguration.stores[0].connection-string}")
  private String connectionString;
    
  private ConfigurationClient getConfigClient(String connectionString) {
    return new ConfigurationClientBuilder().connectionString(connectionString).buildClient();
  }
  
  /*
  * Method to fetch all the configured properties from cloud.
  */
  @Override
  public String fetchCloudConfigurations() {    
    PagedIterable<ConfigurationSetting> configurationSettingList = getConfigClient(connectionString).listConfigurationSettings(new SettingSelector());
    Iterator<ConfigurationSetting> iterator = configurationSettingList.iterator();
    Map<String, String> map = new HashMap<String, String>();
    while(iterator.hasNext()) {
      ConfigurationSetting setting = iterator.next();
      System.out.println(String.format("[fetchCloudConfigurations] Key: %s, Value: %s", setting.getKey(), setting.getValue()));
      map.put(setting.getKey(), setting.getValue());
    }
    return map.toString();
  }
  
  /*
  * Method to create new property in app configuration.
  */
  @Override
  public String createNewKeyValueInCloudConfig(Config config) {
    ConfigurationSetting setting = new ConfigurationSetting();
    setting.setContentType(config.contentType());
    setting.setKey(config.key());
    setting.setLabel(config.label());
    setting.setValue(config.value());
    setting = getConfigClient(connectionString).addConfigurationSetting(setting);
    System.out.println(String.format("[createNewKeyValueInCloudConfig] Key: %s, Value: %s", setting.getKey(), setting.getValue()));
    return "Created";
  }
  
  /*
  * Method to update value of existing property in app configuration.
  */
  @Override
  public String UpdateExistingValueForGivenKeyInCloudConfig(Config config) {
    ConfigurationSetting setting = new ConfigurationSetting();
    setting.setContentType(config.contentType());
    setting.setKey(config.key());
    setting.setLabel(config.label());
    setting.setValue(config.value());
    setting = getConfigClient(connectionString).setConfigurationSetting(setting);
    System.out.println(String.format("[UpdateExistingValueForGivenKeyInCloudConfig] Key: %s, Value: %s", setting.getKey(), setting.getValue()));
    return "Updated";
  }
  
  /*
  * Method to remove particular configuration via its key from app.
  */
  @Override
  public String removeExistingKeyValueFromCloudConfig(Config config) {
    ConfigurationSetting setting = getConfigClient(connectionString).deleteConfigurationSetting(config.key(), config.label());
    System.out.println(String.format("[removeExistingKeyValueFromCloudConfig] Key: %s, Value: %s", setting.getKey(), setting.getValue()));
    return "Removed";
  }
}

Заключение

Вот и все, несколько простых шагов для интеграции приложения конфигурации Azure с приложением микрослужбы Spring Boot. Надеюсь, из этого можно извлечь что-то полезное, если вы хотите иметь внешнюю конфигурацию в своем приложении Spring Boot.

Удачи!