Перейти к основному содержимому

Введение в Spring Cloud Netflix — Эврика

· 8 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

1. Обзор

В этом руководстве мы представим обнаружение сервисов на стороне клиента через « Spring Cloud Netflix Eureka.

Обнаружение служб на стороне клиента позволяет службам находить друг друга и связываться друг с другом без жесткого кодирования имени хоста и порта. Единственной «фиксированной точкой» в такой архитектуре является реестр служб, в котором должна регистрироваться каждая служба.

Одним из недостатков является то, что все клиенты должны реализовать определенную логику для взаимодействия с этой фиксированной точкой. Это предполагает дополнительный сетевой цикл до фактического запроса.

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

Чтобы быть проинформированным о присутствии клиента, они должны отправить в реестр сигнал сердцебиения.

Для достижения цели этого руководства мы реализуем три микросервиса :

  • сервисный реестр ( Eureka Server )
  • служба REST , которая регистрируется в реестре ( Eureka Client )
  • веб-приложение, которое использует службу REST в качестве клиента с поддержкой реестра (клиент Spring Cloud Netflix Feign )

2. Сервер Эврика

Внедрить Eureka Server для сервисного реестра так же просто, как:

  1. добавление spring-cloud-starter-netflix-eureka-server в зависимости
  2. включение сервера Eureka в @SpringBootApplication , аннотируя его с помощью @EnableEurekaServer
  3. настройка некоторых свойств

Давайте сделаем это шаг за шагом.

Сначала мы создадим новый проект Maven и поместим в него зависимости. Обратите внимание, что мы импортируем spring-cloud-starter-parent во все проекты, описанные в этом руководстве:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Мы можем проверить последние выпуски Spring Cloud в документации Spring’s Projects .

Затем мы создадим основной класс приложения:

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

Наконец, мы настроим свойства в формате YAML , поэтому файлом конфигурации будет application.yml :

server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false

Здесь мы настраиваем порт приложения; значение по умолчанию для серверов Eureka — 8761 . Мы говорим встроенному клиенту Eureka не регистрироваться у себя, потому что наше приложение должно действовать как сервер.

Теперь мы укажем в нашем браузере на http://localhost:8761 , чтобы просмотреть панель инструментов Eureka , где мы позже проверим зарегистрированные экземпляры.

На данный момент мы можем видеть основные индикаторы, такие как индикаторы статуса и здоровья:

./e837ab763d2e781898930aeb2e1e4705.png

3. Клиент Эврика

Чтобы приложение @SpringBootApplication было осведомлено об обнаружении, мы должны включить клиент Spring Discovery (например, spring-cloud-starter-netflix-eureka-client ) в наш путь к классам.

Затем нам нужно аннотировать @Configuration с помощью @EnableDiscoveryClient или @EnableEurekaClient. Обратите внимание, что эта аннотация является необязательной, если у нас есть зависимость spring-cloud-starter-netflix-eureka-client от пути к классам.

Последний указывает Spring Boot явно использовать Spring Netflix Eureka для обнаружения служб. Чтобы наполнить наше клиентское приложение некоторыми образцами жизни, мы также включим пакет spring-boot-starter-web в pom.xml и реализуем контроллер REST .

Но сначала мы добавим зависимости. Опять же, мы можем предоставить зависимости spring-cloud-starter-parent определить версии артефакта для нас:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Здесь мы реализуем основной класс приложения:

@SpringBootApplication
@RestController
public class EurekaClientApplication implements GreetingController {

@Autowired
@Lazy
private EurekaClient eurekaClient;

@Value("${spring.application.name}")
private String appName;

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

@Override
public String greeting() {
return String.format(
"Hello from '%s'!", eurekaClient.getApplication(appName).getName());
}
}

И интерфейс GreetingController :

public interface GreetingController {
@RequestMapping("/greeting")
String greeting();
}

Вместо интерфейса мы могли бы также просто объявить сопоставление внутри класса EurekaClientApplication . Однако интерфейс может быть полезен, если мы хотим разделить его между сервером и клиентом.

Затем мы должны настроить application.yml с настроенным именем приложения Spring , чтобы однозначно идентифицировать наш клиент в списке зарегистрированных приложений.

Мы можем позволить Spring Boot выбрать для нас случайный порт, потому что позже мы будем обращаться к этой службе с ее именем.

Наконец, мы должны сообщить нашему клиенту, где он должен найти реестр:

spring:
application:
name: spring-cloud-eureka-client
server:
port: 0
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
instance:
preferIpAddress: true

Мы решили настроить наш Eureka Client таким образом, потому что этот вид сервиса должен быть легко масштабируем в дальнейшем.

Теперь мы запустим клиент и снова укажем в браузере http://localhost:8761 , чтобы увидеть его статус регистрации на панели Eureka Dashboard. Используя Dashboard, мы можем выполнить дальнейшую настройку, например связать домашнюю страницу зарегистрированного клиента с Dashboard для административных целей. Однако параметры конфигурации выходят за рамки этой статьи:

./d31c03ea7c310a72f0a80ad534bb1be6.png

4. Притвориться клиентом

Чтобы завершить наш проект с тремя зависимыми микросервисами, теперь мы реализуем веб-приложение , потребляющее REST , с помощью Spring Netflix Feign Client .

Думайте о Feign как о шаблоне Spring RestTemplate с возможностью обнаружения, использующем интерфейсы для связи с конечными точками. Эти интерфейсы будут автоматически реализованы во время выполнения, и вместо service-urls будут использоваться service-names .

Без Feign нам пришлось бы автоматически подключать экземпляр EurekaClient к нашему контроллеру, с помощью которого мы могли бы получать служебную информацию по имени службы в качестве объекта приложения .

Мы будем использовать это приложение , чтобы получить список всех экземпляров этой службы, выбрать подходящий, а затем использовать эту InstanceInfo , чтобы получить имя хоста и порт. При этом мы могли бы сделать стандартный запрос с любым http-клиентом:

@Autowired
private EurekaClient eurekaClient;

@RequestMapping("/get-greeting-no-feign")
public String greeting(Model model) {

InstanceInfo service = eurekaClient
.getApplication(spring-cloud-eureka-client)
.getInstances()
.get(0);

String hostName = service.getHostName();
int port = service.getPort();

// ...
}

RestTemplate также можно использовать для доступа к клиентским службам Eureka по имени, но эта тема выходит за рамки этой статьи.

Чтобы настроить наш проект Feign Client , мы добавим в его pom.xml следующие четыре зависимости :

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Клиент Feign находится в пакете spring-cloud-starter-feign . Чтобы включить его, мы должны аннотировать @Configuration с помощью @EnableFeignClients . Чтобы использовать его, мы просто аннотируем интерфейс с помощью @FeignClient(“service-name”) и автоматически подключаем его к контроллеру.

Хорошим методом создания таких клиентов Feign является создание интерфейсов с аннотированными методами @RequestMapping и помещение их в отдельный модуль. Таким образом, они могут быть разделены между сервером и клиентом. На стороне сервера мы можем реализовать их как @Controller , а на стороне клиента их можно расширить и аннотировать как @FeignClient . [](/lessons/b/-spring-requestmapping)

Кроме того, в проект необходимо включить пакет spring-cloud-starter-eureka и активировать его, аннотировав основной класс приложения с помощью @EnableEurekaClient .

Зависимости spring-boot-starter-web и spring-boot-starter-thymeleaf используются для представления представления, содержащего данные, извлеченные из нашей службы REST .

Это будет наш интерфейс Feign Client :

@FeignClient("spring-cloud-eureka-client")
public interface GreetingClient {
@RequestMapping("/greeting")
String greeting();
}

Здесь мы реализуем основной класс приложения, который одновременно выступает в роли контроллера:

@SpringBootApplication
@EnableFeignClients
@Controller
public class FeignClientApplication {
@Autowired
private GreetingClient greetingClient;

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

@RequestMapping("/get-greeting")
public String greeting(Model model) {
model.addAttribute("greeting", greetingClient.greeting());
return "greeting-view";
}
}

Это будет шаблон HTML для нашего представления:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Greeting Page</title>
</head>
<body>
<h2 th:text="${greeting}"/>
</body>
</html>

Конфигурационный файл application.yml почти такой же, как и в предыдущем шаге:

spring:
application:
name: spring-cloud-eureka-feign-client
server:
port: 8080
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}

Теперь мы можем построить и запустить этот сервис. Наконец, мы укажем нашему браузеру http://localhost:8080/get-greeting , и он должен отобразить что-то вроде следующего:

Hello from SPRING-CLOUD-EUREKA-CLIENT!

5. « TransportException: невозможно выполнить запрос на любом известном сервере»

При запуске серверов Eureka мы часто сталкиваемся с такими исключениями, как:

com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server

В основном это происходит из-за неправильной настройки в application.properties или application.yml . Eureka предоставляет два свойства для клиента, которые можно настроить:

  • registerWithEureka: Если мы установим для этого свойства значение true, то при запуске сервера встроенный клиент попытается зарегистрироваться на сервере Eureka.
  • fetchRegistry: если мы настроим это свойство как true, встроенный клиент попытается получить реестр Eureka .

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

Если мы отметим вышеуказанные свойства как истинные (или просто не настроим их, так как они истинны по умолчанию), то при запуске сервера встроенный клиент попытается зарегистрироваться на сервере Eureka , а также попытается получить реестр , который пока недоступен. В результате получаем TransportException .

Поэтому мы никогда не должны настраивать эти свойства как истинные в серверных приложениях Eureka . Правильные настройки, которые следует поместить в application.yml :

eureka:
client:
registerWithEureka: false
fetchRegistry: false

6. Заключение

В этой статье мы узнали, как реализовать реестр служб с помощью Spring Netflix Eureka Server и зарегистрировать в нем несколько клиентов Eureka .

Поскольку наш клиент Eureka из шага 3 прослушивает случайно выбранный порт, он не знает его местоположения без информации из реестра. С Feign Client и нашим реестром мы можем найти и использовать службу REST , даже если местоположение меняется.

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

Как обычно, мы можем найти исходники на GitHub , где также есть набор связанных с Docker файлов, которые можно использовать с docker-compose для создания контейнеров из нашего проекта.