«1. Обзор
Spring Cloud — это платформа для создания надежных облачных приложений. Платформа облегчает разработку приложений, предоставляя решения многих распространенных проблем, возникающих при переходе в распределенную среду.
Приложения, работающие с микросервисной архитектурой, призваны упростить разработку, развертывание и обслуживание. Декомпозированный характер приложения позволяет разработчикам сосредоточиться на одной проблеме за раз. Улучшения могут быть введены без воздействия на другие части системы.
С другой стороны, когда мы используем микросервисный подход, возникают другие проблемы:
-
Экстернализация конфигурации, чтобы она была гибкой и не требовала перестроения сервиса при изменении Обнаружение сервисов Скрытие сложности сервисов, развернутых на разных хостах ~~ ~ В этой статье мы создадим пять микрослужб: сервер конфигурации, сервер обнаружения, сервер шлюза, службу книг и, наконец, службу оценки. Эти пять микросервисов образуют надежное базовое приложение для начала разработки облака и решения вышеупомянутых проблем.
2. Сервер конфигурации
При разработке облачного приложения одной из проблем является поддержка и распространение конфигурации для наших сервисов. Мы действительно не хотим тратить время на настройку каждой среды перед горизонтальным масштабированием нашего сервиса или рисковать нарушениями безопасности, внедряя нашу конфигурацию в наше приложение.
Чтобы решить эту проблему, мы объединим всю нашу конфигурацию в один репозиторий Git и подключим его к одному приложению, которое управляет конфигурацией всех наших приложений. Мы собираемся настроить очень простую реализацию.
Чтобы узнать больше деталей и увидеть более сложный пример, ознакомьтесь с нашей статьей Spring Cloud Configuration.
2.1. Настройка
Перейдите на https://start.spring.io и выберите Maven и Spring Boot 2.2.x.
Установите для артефакта значение «config». В разделе зависимостей найдите «config server» и добавьте этот модуль. Затем нажмите кнопку «Создать», и мы сможем загрузить zip-файл с предварительно настроенным проектом внутри и готовым к работе.
Кроме того, мы можем сгенерировать проект Spring Boot и добавить некоторые зависимости в файл POM вручную.
Эти зависимости будут общими для всех проектов:
Давайте добавим зависимость для сервера конфигурации:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Для справки, мы можем найти последнюю версию на Maven Central (spring-cloud -зависимости, тест, конфиг-сервер).
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2.2. Spring Config
Чтобы включить сервер конфигурации, мы должны добавить некоторые аннотации к основному классу приложения:
@EnableConfigServer превратит наше приложение в сервер конфигурации.
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {...}
2.3. Свойства
Давайте добавим application.properties в src/main/resources:
Наиболее важной настройкой для сервера конфигурации является параметр git.uri. В настоящее время установлен относительный путь к файлу, который обычно разрешается в c:\\Users\\{имя пользователя}\\ в Windows или /Users/{имя пользователя}/ в *nix. Это свойство указывает на репозиторий Git, в котором хранятся файлы свойств для всех других приложений. При необходимости можно установить абсолютный путь к файлу.
server.port=8081
spring.application.name=config
spring.cloud.config.server.git.uri=file://${user.home}/application-config
Совет: На машине с Windows предварите значение «file:///», на *nix используйте «file://».
2.4. Репозиторий Git
Перейдите к папке, указанной в spring.cloud.config.server.git.uri, и добавьте папку application-config. компакт-диск в эту папку и введите git init. Это инициализирует репозиторий Git, где мы можем хранить файлы и отслеживать их изменения.
2.5. Run
Запустим сервер конфигурации и убедимся, что он работает. В командной строке введите mvn spring-boot:run. Это запустит сервер.
Мы должны увидеть этот вывод, указывающий на то, что сервер запущен:
2.6. Конфигурация начальной загрузки
Tomcat started on port(s): 8081 (http)
В наших последующих серверах мы хотим, чтобы свойства их приложений управлялись этим сервером конфигурации. Чтобы сделать это, нам на самом деле нужно сделать немного курицы и яйца: настроить свойства в каждом приложении, которое знает, как общаться с этим сервером.
«Это процесс начальной загрузки, и каждое из этих приложений будет иметь файл с именем bootstrap.properties. Он будет содержать такие же свойства, как и application.properties, но с одной изюминкой:
Родительский Spring ApplicationContext сначала загружает bootstrap.properties. Это очень важно, чтобы Config Server мог начать управлять свойствами в application.properties. Именно этот специальный ApplicationContext также будет расшифровывать любые зашифрованные свойства приложения.
Разумно хранить эти файлы свойств отдельно. bootstrap.properties предназначен для подготовки сервера конфигурации, а application.properties — для свойств, специфичных для нашего приложения. Однако технически можно поместить свойства приложения в bootstrap.properties.
Наконец, поскольку Config Server управляет свойствами нашего приложения, может возникнуть вопрос, зачем вообще нужен файл application.properties? Ответ заключается в том, что они по-прежнему пригодятся в качестве значений по умолчанию, которых, возможно, нет в Config Server.
3. Обнаружение
Теперь, когда мы позаботились о конфигурации, нам нужен способ, чтобы все наши серверы могли найти друг друга. Мы решим эту проблему, настроив сервер обнаружения Eureka. Поскольку наши приложения могут работать с любой комбинацией IP/порта, нам нужен центральный реестр адресов, который может служить для поиска адресов приложений.
Когда новый сервер будет предоставлен, он будет связываться с сервером обнаружения и зарегистрирует его адрес, чтобы другие могли общаться с ним. Таким образом, другие приложения могут использовать эту информацию при выполнении запросов.
Чтобы узнать больше деталей и увидеть более сложную реализацию обнаружения, ознакомьтесь со статьей Spring Cloud Eureka.
3.1. Настройка
Снова мы перейдем к start.spring.io. Установите артефакт на «обнаружение». Найдите «eureka server» и добавьте эту зависимость. Найдите «config client» и добавьте эту зависимость. Наконец, сгенерируйте проект.
Кроме того, мы можем создать проект Spring Boot, скопировать содержимое POM с сервера конфигурации и поменять местами эти зависимости:
Для справки, мы найдем пакеты на Maven Central (config-client , эврика-сервер).
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
3.2. Spring Config
Давайте добавим конфигурацию Java в основной класс:
@EnableEurekaServer настроит этот сервер как сервер обнаружения, используя Netflix Eureka. Spring Boot автоматически обнаружит зависимость конфигурации от пути к классам и найдет конфигурацию на сервере конфигурации.
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryApplication {...}
3.3. Свойства
Теперь мы добавим два файла свойств:
Во-первых, мы добавим bootstrap.properties в src/main/resources:
Эти свойства позволят серверу обнаружения запрашивать сервер конфигурации при запуске.
spring.cloud.config.name=discovery
spring.cloud.config.uri=http://localhost:8081
Во-вторых, мы добавляем в наш Git-репозиторий discovery.properties
Имя файла должно соответствовать свойству spring.application.name.
spring.application.name=discovery
server.port=8082
eureka.instance.hostname=localhost
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
Кроме того, мы сообщаем этому серверу, что он работает в зоне по умолчанию, это соответствует настройке региона клиента конфигурации. Мы также говорим серверу не регистрироваться в другом экземпляре обнаружения.
В рабочей среде у нас было бы более одного из них для обеспечения избыточности в случае сбоя, и этот параметр был бы истинным.
Давайте закоммитим файл в репозиторий Git. В противном случае файл не будет обнаружен.
3.4. Добавьте зависимость к серверу конфигурации
Добавьте эту зависимость в файл POM сервера конфигурации:
Для справки, мы можем найти пакет на Maven Central (eureka-client).
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
Добавьте эти свойства в файл application.properties в src/main/resources сервера конфигурации:
3.5. Выполнить
eureka.client.region = default
eureka.client.registryFetchIntervalSeconds = 5
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/
Запустите сервер обнаружения с помощью той же команды mvn spring-boot:run. Вывод из командной строки должен включать:
Остановите и перезапустите службу конфигурации. Если все в порядке, вывод должен выглядеть так:
Fetching config from server at: http://localhost:8081
...
Tomcat started on port(s): 8082 (http)
4. Шлюз
DiscoveryClient_CONFIG/10.1.10.235:config:8081: registering service...
Tomcat started on port(s): 8081 (http)
DiscoveryClient_CONFIG/10.1.10.235:config:8081 - registration status: 204
Теперь, когда мы решили проблемы с конфигурацией и обнаружением, у нас все еще есть проблема с доступом клиентов ко всем нашим приложениям.
«Если мы оставим все в распределенной системе, то нам придется управлять сложными заголовками CORS, чтобы разрешить кросс-происхождение запросов на клиентах. Мы можем решить эту проблему, создав сервер шлюза. Это будет действовать как обратный прокси-сервер, перенаправляющий запросы от клиентов на наши внутренние серверы.
Сервер шлюза — отличное приложение в микросервисной архитектуре, поскольку он позволяет отправлять все ответы с одного хоста. Это устранит необходимость в CORS и даст нам удобное место для решения распространенных проблем, таких как аутентификация.
4.1. Настройка
К настоящему времени мы знаем упражнение. Перейдите на https://start.spring.io. Установите артефакт на «шлюз». Найдите «zuul» и добавьте эту зависимость. Найдите «config client» и добавьте эту зависимость. Найдите «eureka discovery» и добавьте эту зависимость. Наконец, сгенерируйте этот проект.
В качестве альтернативы мы могли бы создать приложение Spring Boot со следующими зависимостями:
Для справки, мы можем найти пакет на Maven Central (config-client, eureka-client, zuul).
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</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-zuul</artifactId>
</dependency>
4.2. Spring Config
Добавим конфигурацию в основной класс:
4.3. Свойства
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class GatewayApplication {...}
Теперь мы добавим два файла свойств:
bootstrap.properties в src/main/resources:
gateway.properties в нашем репозитории Git
spring.cloud.config.name=gateway
spring.cloud.config.discovery.service-id=config
spring.cloud.config.discovery.enabled=true
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/
Свойство zuul.routes позволяет нам определить приложение для маршрутизации определенных запросов на основе сопоставления URL-адресов ant. Наше свойство указывает Zuul направлять любой запрос, поступающий в /book-service/**, в приложение с spring.application.name book-service. Затем Zuul выполнит поиск хоста на сервере обнаружения, используя имя приложения, и перенаправит запрос на этот сервер.
spring.application.name=gateway
server.port=8080
eureka.client.region = default
eureka.client.registryFetchIntervalSeconds = 5
zuul.routes.book-service.path=/book-service/**
zuul.routes.book-service.sensitive-headers=Set-Cookie,Authorization
hystrix.command.book-service.execution.isolation.thread.timeoutInMilliseconds=600000
zuul.routes.rating-service.path=/rating-service/**
zuul.routes.rating-service.sensitive-headers=Set-Cookie,Authorization
hystrix.command.rating-service.execution.isolation.thread.timeoutInMilliseconds=600000
zuul.routes.discovery.path=/discovery/**
zuul.routes.discovery.sensitive-headers=Set-Cookie,Authorization
zuul.routes.discovery.url=http://localhost:8082
hystrix.command.discovery.execution.isolation.thread.timeoutInMilliseconds=600000
Не забудьте зафиксировать изменения в репозитории!
4.4. Запустите
Запустите приложения конфигурации и обнаружения и подождите, пока приложение конфигурации не зарегистрируется на сервере обнаружения. Если они уже запущены, нам не нужно их перезапускать. После этого запустите сервер шлюза. Сервер шлюза должен запускаться на порту 8080 и регистрироваться на сервере обнаружения. Вывод из консоли должен содержать:
Одна ошибка, которую легко сделать, состоит в том, чтобы запустить сервер до того, как сервер конфигурации зарегистрируется в Eureka. В этом случае мы увидим журнал с такими выводами:
Fetching config from server at: http://10.1.10.235:8081/
...
DiscoveryClient_GATEWAY/10.1.10.235:gateway:8080: registering service...
DiscoveryClient_GATEWAY/10.1.10.235:gateway:8080 - registration status: 204
Tomcat started on port(s): 8080 (http)
Это URL-адрес и порт по умолчанию для сервера конфигурации, указывающие на то, что у нашей службы обнаружения не было адреса, когда был сделан запрос конфигурации. Просто подождите несколько секунд и повторите попытку, как только сервер конфигурации зарегистрируется в Eureka, проблема будет решена.
Fetching config from server at: http://localhost:8888
5. Book Service
В микросервисной архитектуре мы можем создавать столько приложений, сколько необходимо для достижения бизнес-целей. Часто инженеры делят свои услуги по областям. Мы будем следовать этому шаблону и создадим книжный сервис для обработки всех операций с книгами в нашем приложении.
5.1. Настройка
Еще раз. Перейдите на https://start.spring.io. Установите артефакт на «book-service». Найдите «web» и добавьте эту зависимость. Найдите «config client» и добавьте эту зависимость. Найдите «eureka discovery» и добавьте эту зависимость. Создайте этот проект.
В качестве альтернативы добавьте эти зависимости в проект:
Для справки, мы можем найти пакет на Maven Central (config-client, eureka-client, web).
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
5.2. Spring Config
Давайте изменим наш основной класс:
Мы также добавили контроллер REST и поле, заданное нашим файлом свойств, чтобы возвращать значение, которое мы установим во время настройки.
@SpringBootApplication
@EnableEurekaClient
@RestController
@RequestMapping("/books")
public class BookServiceApplication {
public static void main(String[] args) {
SpringApplication.run(BookServiceApplication.class, args);
}
private List<Book> bookList = Arrays.asList(
new Book(1L, "Baeldung goes to the market", "Tim Schimandle"),
new Book(2L, "Baeldung goes to the park", "Slavisa")
);
@GetMapping("")
public List<Book> findAllBooks() {
return bookList;
}
@GetMapping("/{bookId}")
public Book findBook(@PathVariable Long bookId) {
return bookList.stream().filter(b -> b.getId().equals(bookId)).findFirst().orElse(null);
}
}
Теперь добавим книгу POJO:
5.3. Свойства
public class Book {
private Long id;
private String author;
private String title;
// standard getters and setters
}
Теперь нам просто нужно добавить два файла свойств:
bootstrap.properties в src/main/resources:
book-service.properties в наш репозиторий Git:
spring.cloud.config.name=book-service
spring.cloud.config.discovery.service-id=config
spring.cloud.config.discovery.enabled=true
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/
Зафиксируем изменения в репозитории.
spring.application.name=book-service
server.port=8083
eureka.client.region = default
eureka.client.registryFetchIntervalSeconds = 5
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/
5.4. Запустить
После того, как все остальные приложения запустятся, мы можем запустить книжный сервис. Вывод консоли должен выглядеть так:
«
DiscoveryClient_BOOK-SERVICE/10.1.10.235:book-service:8083: registering service...
DiscoveryClient_BOOK-SERVICE/10.1.10.235:book-service:8083 - registration status: 204
Tomcat started on port(s): 8083 (http)
«Как только он будет запущен, мы можем использовать наш браузер для доступа к конечной точке, которую мы только что создали. Перейдите к http://localhost:8080/book-service/books, и мы получим объект JSON с двумя книгами, которые мы добавили в наш контроллер. Обратите внимание, что мы не обращаемся к сервису книг напрямую через порт 8083, а идем через сервер шлюза.
6. Служба оценки
Как и наша книжная служба, наша служба оценки будет управляемой доменом службой, которая будет обрабатывать операции, связанные с оценками.
6.1. Настройка
Еще раз. Перейдите на https://start.spring.io. Установите для артефакта значение «rating-service». Найдите «web» и добавьте эту зависимость. Найдите «config client» и добавьте эту зависимость. Найдите «eureka discovery» и добавьте эту зависимость. Затем создайте этот проект.
В качестве альтернативы добавьте эти зависимости в проект:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Для справки, мы можем найти пакет на Maven Central (config-client, eureka-client, web).
6.2. Spring Config
Давайте изменим наш основной класс:
@SpringBootApplication
@EnableEurekaClient
@RestController
@RequestMapping("/ratings")
public class RatingServiceApplication {
public static void main(String[] args) {
SpringApplication.run(RatingServiceApplication.class, args);
}
private List<Rating> ratingList = Arrays.asList(
new Rating(1L, 1L, 2),
new Rating(2L, 1L, 3),
new Rating(3L, 2L, 4),
new Rating(4L, 2L, 5)
);
@GetMapping("")
public List<Rating> findRatingsByBookId(@RequestParam Long bookId) {
return bookId == null || bookId.equals(0L) ? Collections.EMPTY_LIST : ratingList.stream().filter(r -> r.getBookId().equals(bookId)).collect(Collectors.toList());
}
@GetMapping("/all")
public List<Rating> findAllRatings() {
return ratingList;
}
}
Мы также добавили контроллер REST и поле, заданное нашим файлом свойств, чтобы возвращать значение, которое мы установим во время настройки.
Добавим рейтинг POJO:
public class Rating {
private Long id;
private Long bookId;
private int stars;
//standard getters and setters
}
6.3. Свойства
Теперь нам просто нужно добавить два файла свойств:
bootstrap.properties в src/main/resources:
spring.cloud.config.name=rating-service
spring.cloud.config.discovery.service-id=config
spring.cloud.config.discovery.enabled=true
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/
rating-service.properties в наш репозиторий Git:
spring.application.name=rating-service
server.port=8084
eureka.client.region = default
eureka.client.registryFetchIntervalSeconds = 5
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/
Зафиксируем изменения в репозитории.
6.4. Запустить
После того, как все остальные приложения запустятся, мы можем запустить службу рейтинга. Вывод консоли должен выглядеть так:
DiscoveryClient_RATING-SERVICE/10.1.10.235:rating-service:8083: registering service...
DiscoveryClient_RATING-SERVICE/10.1.10.235:rating-service:8083 - registration status: 204
Tomcat started on port(s): 8084 (http)
После того, как он запущен, мы можем использовать наш браузер для доступа к конечной точке, которую мы только что создали. Перейдите по адресу http://localhost:8080/rating-service/ratings/all, и мы получим JSON со всеми нашими оценками. Обратите внимание, что мы не обращаемся к службе рейтинга напрямую через порт 8084, а проходим через шлюзовой сервер.
7. Заключение
Теперь мы можем соединить различные части Spring Cloud в работающее микросервисное приложение. Это формирует основу, которую мы можем использовать для создания более сложных приложений.
Как всегда, мы можем найти этот исходный код на GitHub.