Методология разработки приложений с двенадцатью факторами настоятельно рекомендует «строгое отделение конфигурации от кода» [1]

Spring Cloud предлагает решение этой проблемы с помощью Spring Cloud Config Server. Spring Cloud Config Server определяет себя следующим образом.

«Spring Cloud Config обеспечивает поддержку на стороне сервера и клиента для внешней конфигурации в распределенной системе. С помощью Config Server у вас есть центральное место для управления внешними свойствами приложений во всех средах »[2]

Централизованное управление конфигурацией - очень простая и очень эффективная функция. Отсутствие централизованной настройки очень часто приводит к ошибкам.

Еще одна хорошая функция - после изменения конфигурации вы можете легко получить их из своего приложения без перезапуска. Существует множество руководств, демонстрирующих, как создать сервер конфигурации и обновить свойство во время выполнения. Я считаю, что ПОЧЕМУ-СТАТЬИ важнее, чем КАК-СТАТЬЯ. Поэтому я не утверждаю, как создать сервер конфигурации и т. Д. Я хочу описать, почему мы используем свойства конфигурации при обновлении свойств во время выполнения.

Если вы выполните поиск в сети, есть множество ресурсов, которые просто показывают область обновления на контроллере, как показано ниже.

@EnableAutoConfiguration
@ComponentScan
@RestController
@RefreshScope // important!
public class SpringApp {
    @Value("${bar:World!}")
    String bar;

    @RequestMapping("/")
    String hello() {
        return "Hello " + bar + "!";
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringApp.class, args);
    }
}

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

test:
    value: MyTestValue

Контроллер:

@RestController("/test")
public class TestController {

    private final TestService testService;

    public TestController(TestService testService) {
        this.testService = testService;
    }

    @GetMapping("/v1")
    public String test() {
        return testService.getValueWithDelay();
    }

    @GetMapping("/v2")
    public String test2()  {
        return testService.getValue();
    }
}

Услуга:

@RefreshScope
@Service
public class TestService {

    @Value("${test.value}")
    private String value;

    public String getValueWithDelay() {
        try {
            Thread.sleep(30000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return value;
    }

    public String getValue() {
        return value;
    }
}

Звоните http://127.0.0.1:8080/test/v1. Задержка составляет 30 секунд. Он возвращается через 30 секунд.

Звоните «http://127.0.0.1:8080/test/v2. Он возвращается через 6 миллисекунд.

Вроде все идеально. Давайте изменим сценарий.

  • Звоните http://127.0.0.1:8080/test/v1
  • Измените тестовое значение на MyTestValueChanged
  • Обновить свойства localhost: 8080 / активатор / обновить
  • Звонок «http://127.0.0.1:8080/test/vpа2

Конечная точка V2 возвращается через 10–20 секунд или около того. (Эти операции необходимо завершить до завершения выполнения версии 1)

Мы использовали область обновления в классе обслуживания. Если значение, которое используется внутри класса обслуживания, то другие методы будут ждать, пока это значение не будет заменено контекстом. В нашей современной среде разработки программного обеспечения важны даже миллисекунды. V1 и V2 - разные операции, но они блокируют друг друга из-за использования области обновления. Хуже того, очень проблематичным становится отслеживание того, какое значение повлияет на какую услугу.

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

@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "test")
public class TestConfiguration {

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

Услуга:

@Service
public class TestService {
    private final TestConfiguration testConfiguration;

    public TestService(TestConfiguration testConfiguration) {
        this.testConfiguration = testConfiguration;
    }

    public String getValueWithDelay() {
        try {
            Thread.sleep(30000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return testConfiguration.getValue();
    }

    public String getValue() {
        return testConfiguration.getValue();
    }
}

Конечная точка V2 возвращается через 10 миллисекунд или около того. Нет блоков.

Задача решена.

PS: Особая благодарность Эркану Сормазу.

Использованная литература:

  1. Https://12factor.net/config
  2. Https://cloud.spring.io/spring-cloud-config/reference/html/