Spring Cloud Configuration Server

Для одного Spring Boot приложения нормально хранить настройки в локальном application.properties. Но если приложений несколько (микросервисов), и они используют общие настройки, то неплохо бы их вынести в одно общее место. И это можно сделать.
Благодаря Configuration Server настройки можно хранить в Git-репозитории. И к тому же менять их прямо во время работы микросервисов без их перезапуска.

Введение

Configuration Server — это еще одно Spring Boot приложение. Тоже микросервис, к которому остальные микросервисы обращаются за настройками. Есть несколько решений для Configuration Server: Apache Zookeeper, ETCD, Hashicorp Consul, но мы рассмотрим Spring Cloud Configuration Server.

Configuration Server мог бы хранить настройки в своем локальном application.properties и отдавать их другим микросервисам из него, но это не лучший вариант. Потому что при изменении настроек пришлось бы перезапускать Configuration Server, а мы стремимся к бесперебойной работе. Поэтому самое популярное — хранить их в репозитории (например, Git):

Тогда, чтобы поменять настройки на production, не надо ничего перезапускать. Достаточно отправить новые настройки в репозиторий. Дополнительный плюс такого варианта — сохраняется история настроек.

Продолжим совершенствовать пример с двумя микросервисами Zoo и Random Animal. Мы уже внедрили Eureka, API Gateway. А теперь вынесем конфигурацию на GitHub.

Создание Spring Cloud Config Server

Создадим новое Spring Boot приложение и добавим в него зависимость Config Server:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

Описание  на https://start.spring.io/ говорит о том, какие хранилища поддерживает Config Server:

Central management for configuration via Git, SVN, or HashiCorp Vault.

У нас будет Git.

Аннотация главного класса

Необходимо также аннотировать главный класс приложения аннотацией @EnableConfigServer:

@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {

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

}

Указание пути до репозитория Git

Репозиторий можно создать на локальном ПК или на GitHub. У нас будет репозиторий на GitHub:

https://github.com/myluckagain/config.git

В нем лежит файл application.properties с содержимым:

greeting=hi

Чтобы Configuration Server знал, откуда брать настройки,  путь до репозитория с этим файлом надо указать в файле настроек нашего Configuration Server:

spring.cloud.config.server.git.uri=https://github.com/myluckagain/config.git

Запуск Configuration Server

Теперь можно запустить приложение и убедиться, что настройки берутся из репозитория. Только еще укажем стандартный для Configuration Server порт 8888 (8080 будет занят другим микросервисом):

server.port=8888

Запустим приложение и откроем в браузере страницу:

http://localhost:8888/application/default

здесь application — имя файла (без расширения), default — имя профиля (по умолчанию default).

На экран будет выведена настройка greeting, взятая с GitHub:

Настройки вытаскиваются
Настройки корректно извлекаются

Теперь чтобы поменять настройку, достаточно отправить в репозиторий новую версию файла application.properties. Перезапускать сервер не нужно.

Итак, Configuration Server подтягивает настройки и выдает их правильно. Теперь сделаем там, чтобы и другие микросервисы могли использовать эти настройки. Сделаем их клиентами сервера конфигурации.

Создание Spring Cloud Config Client

Чтобы сделать микросервис Zoo клиентом, добавим в него зависимость:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

Клиент должен знать, где находится сервер, с которого ему брать настройки. Так что зададим адрес сервера в файле application.properties клиента (микросервиса Zoo):

spring.cloud.config.uri=http://localhost:8888

И с конца 2020, чтобы все заработало, нужно еще добавить в клиент зависимость :

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

Теперь Zoo будет брать настройки с Configuration Server. Чтобы в этом убедиться, добавим в локальный файл application.properties микросервиса Zoo аналогичное свойство greeting, но с другим значением (не тем, что в файле application.properties на GitHub):

greeting=local hi

И исправим контроллер: добавим вывод свойства greeting рядом с именем животного:

@RestController
public class ZooController {
    @Autowired
    private RandomAnimalClient randomAnimalClient;

     @Value("${greeting}")
     String greeting;

    @GetMapping("/animals/any")
    Animal seeAnyAnimal(){
         Animal animal=randomAnimalClient.random().getBody();
         animal.setName(this.greeting+", "+animal.getName());
         return animal;
    }
}

Теперь все запустим и убедимся, что свойство берется из удаленного репозитория, а не из локального файла application.properties:

Результат - приветствие hi берется из удаленного репозитория
Результат — приветствие hi берется из удаленного репозитория

Основное сделано, но рассмотрим еще один момент.

Разные значения настройки для разных микросервисов

Мы вынесли настройки для всех микросервисов в общий репозиторий. Но что, если разным микросервисам требуются разные значения одного свойства (например, порта)? Такую настройку тоже можно вынести в репозиторий, но только положить в отдельный файл с именем конкретного микросервиса.

Например, настройки для микросервиса Zoo нужно положить в zoo.properties.

Имя микросервиса — это то, что задается в свойстве spring.application.name=zoo. Имена мы задавали в статье про Eureka.

Уже упомянутый порт мы выносить не будем, но зададим другое свойство — специфичный для Zoo greeting в новом файле zoo.properties:

greeting=zoo hi

И отправим новый файл в репозиторий.

Теперь приветствие берется из нового файла:

Изменение настроек: обновление на Config-сервере и на клиенте

Если отправить в репозиторий новую версию zoo.properties, то на сервере конфигурации она отобразится мгновенно, без перезапуска сервера. Сервер для того и предназначен, чтобы следить за настройками.

Новый zoo.properties:

greeting=newly updated zoo hi

Обновленный сервер http://localhost:8888/zoo/default:

С остальными микросервисами ситуация другая. Они созданы для других задач и не обязаны постоянно опрашивать сервер на предмет изменений. Чтобы настройка отобразилась на микросервисе-клиенте, его надо перезапустить.

Но можно это сделать и без перезапуска, как объявлялось в самом начале статьи. Для этого придумана конечная точка в Spring Actuator. Когда мы хотим, чтоб клиент обновил свои настройки, мы делаем POST-запрос на эту точку, и клиент обновляет настройки.

Добавление Spring Actuator

Прежде всего, добавим Spring Actuator в клиент — в микросервис Zoo:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Открыть точку /actuator/refresh

Важный момент: надо открыть конечную точку refresh, чтобы на нее можно было делать запросы. Для этого создадим файл bootstrap.properties и пропишем в нем:

spring.application.name=zoo
spring.cloud.config.uri=http://localhost:8888
management.endpoints.web.exposure.include=refresh

Аннотация @RefreshScope

Но это не все: нужно аннотировать тот класс, чьи настройки мы хотим обновлять по POST-запросу. Добавим аннотацию @RefreshScope в ZooController:

@RestController
@RefreshScope
public class ZooController {
    @Autowired
    private RandomAnimalClient randomAnimalClient;

    @Value("${greeting}")
    String greeting;

    @GetMapping("/animals/any")
    Animal seeAnyAnimal() {
        Animal animal = randomAnimalClient.random().getBody();
        animal.setName(this.greeting + ", " + animal.getName());
        return animal;
    }
}

POST-запрос на точку /actuator/refresh

И сделаем POST-запрос. Zoo запущен на порту 8081, так что запрос делаем по адресу:

localhost:8081/actuator/refresh

Запрос сделаем с помощью Postman:

В ответе есть обновленное свойство. Теперь обновим страницу клиента:

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

Итоги

Обновленный пример с сервером конфигурации можно скачать на GitHub.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *