Для одного 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:
У нас будет 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:
Основное сделано, но рассмотрим еще один момент.
Разные значения настройки для разных микросервисов
Мы вынесли настройки для всех микросервисов в общий репозиторий. Но что, если разным микросервисам требуются разные значения одного свойства (например, порта)? Такую настройку тоже можно вынести в репозиторий, но только положить в отдельный файл с именем конкретного микросервиса.
Например, настройки для микросервиса Zoo нужно положить в zoo.properties.
Уже упомянутый порт мы выносить не будем, но зададим другое свойство — специфичный для 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.