Статьи

Spring Cloud – базовая настройка

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

1. Первые услуги

Давайте определим проблему, которую мы будем решать с помощью Spring Cloud. Целью этой демонстрации является создание основы распределенной платформы для блогов.

Основным компонентом распределенной системы является служба, которая представляет собой не что иное, как обычное приложение, предназначенное для сосредоточения на определенной части домена. В сложной системе может быть множество разных сервисов, но для ясности нашей демонстрации мы начнем с двух. Первый сервис будет заботиться об авторах, а второй – о статьях.

1.1. Авторский сервис

В нашем случае служба автора – это типичное приложение Spring Boot, созданное с помощью spring-boot-starter-web . На данный момент мы не используем никаких функций Spring Cloud.

1
2
3
4
5
6
7
8
@SpringBootApplication
public class AuthorServiceApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(AuthorServiceApplication.class, args);
    }
 
}

Вот авторский класс домена, который является основным направлением нашего первого сервиса.

1
2
3
4
5
6
7
8
class Author {
 
    private final Long id;
    private final String name;
 
    //…
 
}

Наконец, мы создаем REST-контроллер, который позволяет выбрать всех авторов или найти конкретного по его идентификатору.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@RestController
class AuthorController {
 
    //…
 
    @GetMapping
    List<Author> findAll() {
        //…
    }
 
    @GetMapping("/{id}")
    Author findOne(@PathVariable long id) {
        //…
    }
 
}

1.2. Статья службы

Второй сервис аналогичен предыдущему. Если вам нужны примеры кода, вы можете найти их в репозитории GitHub .

Ключевым моментом этого шага является осознание того, что вместо одного более крупного приложения мы разделяем различные части домена на более мелкие и слабо связанные приложения. Что это нам дает? Есть много преимуществ, таких как, например, более простая масштабируемость, отказоустойчивость или более быстрое развертывание. Если вам нужно больше теоретических знаний, я рекомендую вам проверить замечательную книгу Сэма Ньюмана «Создание микросервисов» .

2. Распределенные конфигурации

Если вы попытаетесь запустить обе службы на одном компьютере, это будет невозможно с настройками Spring Boot по умолчанию, поскольку оба приложения будут пытаться работать на порте 8080. Вы можете настроить параметры и выбрать разные порты в application.properties каждого из них. приложение, которое для двух служб не будет проблемой, но для десятков это может быть более проблематичным.

2.1. Конфигурационный сервер

Для сложных распределенных систем разумнее хранить конфигурации для всех служб в одном месте, чтобы упростить весь процесс управления. Как и положено для микросервисной системы, эти конфигурации будут обслуживаться … другой службой. Создайте приложение и поместите следующую зависимость в ваш файл pom.xml .

1
2
3
4
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

Основной класс приложения на сервере конфигурации не сильно отличается от других приложений. Единственное отличие – аннотация @EnableConfigServer от ранее добавленной зависимости Spring Cloud, которая отвечает за предоставление API для внешних конфигураций.

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
    }
 
}

2.2. Расположение конфигурации

Где мы будем хранить конфигурации для наших услуг? Объединение свойств в файле JAR сервера конфигурации не будет гибким решением. Некоторое внешнее местоположение кажется лучшей идеей. По умолчанию Spring Cloud использует Git-репозиторий для управления конфигурациями. Адрес URI (и другие подробности) сервера Git можно установить в файле application.properties приложения конфигурации. К счастью, в нашей демонстрации нам не нужен отдельный Git-сервер. В целях тестирования локальный репозиторий работает просто отлично.

1
2
3
4
server.port=9001
spring.application.name=config-server
 
spring.cloud.config.server.git.uri=file://${user.home}/config

Расположение репозитория Git задается с помощью свойства spring.cloud.config.server.git.uri . Для использования реального сервера значение должно быть изменено на некоторый URL без префикса file: Мы также изменили порт по умолчанию, чтобы избежать конфликтов с другими службами при работе на одной машине. Кроме того, приложение получило собственное имя. На данный момент это не нужно, но позже мы будем использовать это имя как ссылку на сервер конфигурации в других сервисах.

2,3. Хранилище настроек

Конфигурации для всех сервисов будут храниться в месте, указанном в spring.cloud.config.server.git.uri . На данный момент мы можем создать два файла, предназначенных для ранее разработанных сервисов, в которых мы изменим порт по умолчанию и назначим имена так же, как мы сделали для сервера конфигурации.

Вот содержимое файла article-service.properties .

1
2
server.port=9003
spring.application.name=article-service

Файл author-service.properties на данный момент выглядит практически идентично.

1
2
server.port=9004
spring.application.name=author-service

Наконец, инициализируйте репозиторий Git и зафиксируйте обе созданные конфигурации.

1
2
3
git init
git add .
git commit -m 'Service configs'

3. Обнаружение службы

Сервер конфигурации готов, но другие службы до сих пор не знают о его существовании и о том, как получить их конфигурации. Одним из возможных решений этой проблемы является прямое соединение сервисов с сервером с помощью Spring Cloud Config Client, который можно добавить с зависимостью spring-cloud-starter-config . Основным недостатком является то, что нам придется жестко задавать адрес сервера конфигурации в каждой службе. Если местоположение этой службы изменится в будущем или мы хотим предоставить избыточные одноранговые узлы, все службы потребуют обновления. Проблема поиска других сервисов в распределенной системе решается с помощью шаблона, называемого Service Discovery.

3.1. Сервер реестра

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

01
02
03
04
05
06
07
08
09
10
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
</dependencies>

Первый отвечает за раскрытие API реестра. Вторая зависимость уже упоминалась и используется для соединения с нашим сервером конфигурации, который также будет содержать конфигурацию для реестра, который мы создаем. Основной класс приложения похож на другие приложения Spring Boot. Мы просто добавляем аннотацию @EnableEurekaServer для предоставления API реестра.

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableEurekaServer
public class RegistryServerApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(RegistryServerApplication.class, args);
    }
 
}

Последнее, что не хватает на сервере реестра – это настройка начальной загрузки. Основная часть конфигурации будет обслуживаться сервером конфигурации, но нам нужно описать, как его найти. Создайте файл bootstrap.properties в каталоге main / resources и добавьте строки, представленные ниже, которые являются адресом сервера конфигурации и именем приложения реестра, используемого для получения свойств.

1
2
spring.cloud.config.name=registry-server
spring.cloud.config.uri=http://localhost:9001

3.2. Предоставление конфигурации реестра

Следующим шагом является добавление конфигурации в наш Git-репозиторий, отслеживаемый сервером конфигурации. Создайте файл с именем registry-server.properties . Важно, чтобы имя файла совпадало со значением свойства spring.cloud.config.name в файле bootstrap.properties в приложении сервера реестра. Минимальная требуемая конфигурация представлена ​​ниже. Не забудьте внести изменения в репозиторий Git.

1
2
3
4
5
6
spring.application.name=registry-server
server.port=9002
 
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:9002/eureka/

Первые два свойства типичны для обычного приложения Spring Boot. Остальные три посвящены Spring Cloud Eureka Client. По умолчанию каждый сервер Eureka пытается подключиться к другим одноранговым серверам, чтобы зарегистрировать свое присутствие. В нашей простой демонстрации у нас есть только один экземпляр сервера реестра, но в производственном решении вы, вероятно, обеспечите некоторую избыточность такой службы. Наш сервер реестра ни к чему не подключится, поэтому мы меняем значения по умолчанию на false. События регистрации распространяются на все серверы Eureka, перечисленные в eureka.client.serviceUrl.defaultZone , но, хотя есть только один, как в нашем случае, нам все еще нужно установить это свойство, чтобы переопределить значение по умолчанию.

3.3. Запуск реестра с внешней конфигурацией

В этот момент мы можем запустить оба сервера, чтобы проверить, работают ли они должным образом. Поскольку сервер реестра зависит от сервера конфигурации, его необходимо запустить в первую очередь. Через несколько секунд сервер реестра также может быть запущен. Если вы не пропустили ни одного шага, оба приложения должны работать без ошибок в журналах. Сервер конфигурации должен получить конфигурацию и запустить через порт 9002. После перехода по адресу http: // localhost: 9002 / на панели инструментов Eureka отобразится некоторая информация о работающем экземпляре.

4. Настройка службы регистрации

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

1
2
3
4
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

Это позволит нам использовать аннотацию клиента Eureka в основном классе сервера конфигурации.

1
2
3
4
@EnableEurekaClient
public class ConfigServerApplication {
    //…
}

Последнее – это адрес сервера реестра, который необходимо добавить в application.properties сервера конфигурации.

1
eureka.client.serviceUrl.defaultZone=http://localhost:9002/eureka/

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

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

5. Выбор конфигурации

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

  • Добавление зависимости от spring-cloud-starter-eureka
  • Аннотирование основного класса с помощью @EnableEurekaClient
  • Установка eureka.client.serviceUrl.defaultZone в bootstrap.properties (не в application.properties )

Это позволит службам обмениваться данными с сервером реестра, но все равно не получит никакой конфигурации. Чтобы автоматизировать этот процесс, нам нужна еще одна небольшая зависимость в наших службах (обратите внимание, та же самая используется на сервере реестра).

1
2
3
4
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

Наконец, нам нужно установить некоторые дополнительные детали в bootstrap.properties . Вот пример файла для сервиса Author. Аналоговые свойства должны быть добавлены в сервис Article.

1
2
3
spring.cloud.config.name=author-service
spring.cloud.config.discovery.service-id=config-server
spring.cloud.config.discovery.enabled=true

Значение spring.cloud.config.name должно совпадать с соответствующим файлом свойств в хранилище конфигурации, обслуживаемом сервером конфигурации. Второе свойство используется для идентификации сервера конфигурации, зарегистрированного на нашем сервере Eureka. Это значение для изменения должно совпадать со значением spring.application.name, присутствующим в application.properties на сервере конфигурации. С последним свойством мы включаем процесс обнаружения конфигурации.

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

6. Одна Служба, чтобы управлять ими всеми, Одна Служба, чтобы найти их

В этот момент мы могли бы считать нашу базовую настройку готовой, но мы собираемся добавить еще одну часть в головоломку и узнать о другом паттерне, используемом в распределенной системе, называемой Service Gateway. Как следует из названия, его цель – позволить клиентам находить все сервисы с единой точкой доступа. Другими словами, шлюз действует как маршрутизатор для распределенных сервисов.

6.1. Шлюз сервис

Давайте создадим последнее приложение Spring Boot в этой демонстрации и добавим зависимости, упомянутые ниже. Единственный новый – это spring-cloud-starter-zuul, который содержит классы, необходимые для создания нашего шлюза.

01
02
03
04
05
06
07
08
09
10
11
12
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

В дополнение к ранее добавленным аннотациям основной класс приложения должен также использовать @EnableZuulProxy, чтобы объявить себя в качестве прокси-шлюза.

01
02
03
04
05
06
07
08
09
10
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class GatewayServiceApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(GatewayServiceApplication.class, args);
    }
 
}

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

1
2
3
4
5
spring.cloud.config.name=gateway-service
spring.cloud.config.discovery.service-id=config-server
spring.cloud.config.discovery.enabled=true
 
eureka.client.serviceUrl.defaultZone=http://localhost:9002/eureka/

6.2. Загрузка конфигурации

Как и для других обычных служб, шлюз зависит от конфигурации, хранящейся в хранилище Git, управляемом сервером конфигурации. Создайте файл с именем gateway-service.properties , задайте для его содержимого следующие значения, а затем передайте изменения в хранилище конфигурации.

1
2
3
4
5
6
spring.application.name=gateway-service
server.port=8080
 
zuul.routes.author-service.path=/authors/**
 
zuul.routes.article-service.path=/articles/**

Мы уже знаем первые два значения. Два других более интересны. Здесь мы определяем, что для заданных шаблонов URL все запросы к шлюзу должны перенаправляться службам, идентифицированным по их именам. Обратите внимание, что мы не связываем шлюз с конкретными адресами хостов этих сервисов. Шлюз найдет их, используя ранее созданный сервис обнаружения.

6.3. Окончательная проверка базовой настройки

После запуска шлюза приложение должно прослушивать порт 8080. Попробуйте получить доступ к обеим службам домена с настроенными сопоставлениями:

Http: // Localhost: 8080 / статьи
Http: // Localhost: 8080 / авторы

Шаблон шлюза позволяет нам отделить клиентов нашего API от определенных хостов, на которых работают сервисы. Только адрес шлюза должен быть разделен с клиентами. Шлюз также может позаботиться о балансировке нагрузки для дублированных служб, но давайте оставим эту тему на другой раз.

7. Резюме

На первый взгляд базовая установка Spring Cloud может показаться довольно сложной, особенно по сравнению с типовой базой монолитных приложений. Есть еще много строительных блоков, которые создают систему. Тем не менее, каждый компонент имеет свое собственное назначение, разработанное с учетом принципа единой ответственности. Мы узнали о трех основных шаблонах, используемых в архитектуре микросервиса: обнаружение служб, распределенная конфигурация и шлюз служб. У каждого из них есть специальное приложение, которое фокусируется только на одной задаче. Разделение обязанностей является основной сущностью микросервисной архитектуры, и даже небольшая демонстрация, которую мы создали, в значительной степени отражает эту идею на практике.

Смотреть оригинальную статью здесь: Spring Cloud – базовая настройка

Мнения, высказанные участниками Java Code Geeks, являются их собственными.