Источник: Nuances of Programming
В Java HTTP-запросы между сервисами реализуются весьма просто. Так как существует ряд известных открытых HTTP-клиентов, таких как OkHttp и RestTemplate в Spring, то сложность представляет не выбор подходящего кандидата, а дальнейшая с ним работа.
Среди растущего числа распределенных облачных сервисов, где сервера постоянно появляются и исчезают, конечные точки сервисов оказываются динамичны и их сложно знать наперед. По этой причине, прежде чем отправлять запрос сервису, REST-клиенту необходимо интегрироваться в реестр сервисов и находить там его конечную точку.
Помимо этого, нужно обработать сериализацию запрос-ответ, а также реализовать балансировку нагрузки, правильно распределив ее между серверами. Плюсом же к этому нужно организовать систему отказоустойчивости. Все перечисленное представляет собой сквозную функциональность распределенных систем.
Именно здесь на помощь приходит Spring Cloud OpenFeign. Это не просто HTTP-клиент, а целостное решение задач, сопутствующих современным REST-клиентам. Spring Cloud OpenFeign обеспечивает интеграцию OpenFeign для Spring Boot путем автоматической настройки и привязки к среде Spring.
Сервис OpenFeign, изначально известный как Feign, является детищем Netflix. Он позволяет разработчикам использовать декларативный способ построения HTTP-клиентов с помощью аннотированных интерфейсов и без шаблонного кода. Spring Cloud OpenFeign обеспечивает балансировку нагрузки с помощью Ribbon и очень удобно интегрируется с другими облачными службами, например с Eureka для обнаружения сервисов и Hystrix для отказоустойчивости. Все это предусмотрено в OpenFeign изначально и не требует прописывания дополнительного кода.
В текущей статье вы узнаете:
- Как с помощью Spring Cloud OpenFeign построить декларативный легко читаемый REST-клиент для вызова сервисов по HTTP.
- Как настроить Ribbon и конечные точки для балансировки нагрузки.
- Как активировать Eureka-клиент в вашем сервисе Spring REST для интеграции с Eureka Server.
Создание Maven-проекта
Для генерации Maven-проекта со Spring Boot 2.x можно использовать Spring Initializr. Добавьте в этот проект зависимости Spring Web, OpenFeign и Ribbon.
Если вы начинаете Maven-проект с нуля, то импортируйте Spring Cloud Dependencies POM, чтобы он наследовал все версии артефактов семейства Spring CLoud.
Далее добавьте к зависимостям проекта модули Spring Boot Starter Web, Spring Cloud Starter OpenFeign и Spring Cloud Starter Netflix Ribbon.
В качестве альтернативы можете загрузить весь проект с GitHub .
Создание класса Application для запуска
Как и в любом приложении Spring Boot, для запуска ApplicationContext необходим основной класс. Создайте его с помощью аннотации @SpringBootApplication , добавив главный метод, вызывающий SpringApplication.run() для запуска приложения.
@SpringBootApplication
@EnableFeignClients
public class Application {
public static final void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication активирует в приложении сканирование компонентов и автоматическую настройку. Аннотацию @EnableFeignClients мы добавляем, чтобы включить сканирование компонентов для интерфейсов, аннотированных с @FeignClient .
Создание интерфейса REST-клиента
Создайте интерфейс PostmanEchoClient , добавьте к нему аннотацию @FeinClient и назовите ее postman-echo . Spring автоматически просканирует наш интерфейс и создаст реализацию для REST-клиента в среде выполнения.
@FeignClient(name = "postman-echo")
public interface PostmanEchoClient {
}
Spring использует имя postman-echo , сопровождаемое аннотацией @FeignClient в качестве идентификатора, чтобы создать RibbonClient для клиентского балансировщика нагрузки.
Есть несколько способов предоставить RibbonClient конечную точку сервера для балансировки нагрузки, например использовать настройки Java, свойства приложения Spring или интеграцию с Eureka Server для поиска этой конечной точки.
Чтобы перейти от жестко закодированного URL конечной точки сервера к решению с балансировкой нагрузки, давайте настроим Ribbon со статическим listOfServers . В разделе src/main/resources файла application.yaml добавьте следующие свойства:
postman+echo:
ribbon:
listOfServers: https://postman-echo.com/, https://postman-echo.com/
Postman Echo — это сервис, который можно использовать для тестирования REST-клиентов и совершения пробных вызовов API. Он предоставляет конечные точки для GET , POST и PUT с различными механизмами аутентификации.
На официальном сайте Postman Echo можно найти всю документацию по конечным точкам вместе с примерами ответов.
Добавление клиентского метода GET-запроса
Далее добавьте в интерфейсе клиента Postman Echo клиентский метод getEcho , который принимает параметры запроса String foo и String bar , возвращая объект EchoGetResponse . Прикрепите к этому GET-запросу path /get при помощи аннотации GetMapping . При вызове этот метод будет вызывать конечную точку GET-запроса Postman Echo.
@GetMapping(
path = "/get",
consumes = "application/json")
EchoGetResponse getEcho(
@RequestParam("foo") String foo,
@RequestParam("bar") String bar
);
Конечная точка GET-запроса Postman Echo будет повторять все переданные параметры запроса в теле ответа в элементе args . Документация по GET-запросу Postman Echo лежит здесь .
{
"args": {
"foo": "abc",
"bar": "123"
}
}
Создайте новый класс EchoGetResponse , который будет представлять ответ JSON. EchoGetResponse — это класс простого Java-объекта (POJO). Ответ JSON будет десериализован в нашем методе GET-запроса Postman Echo как EchoGetResponse .
Тестирование метода GET-запроса с помощью ‘SpringBootTest’
Мы тестируем вызов клиентского метода GET-запроса к удаленной конечной точке с помощью SpringBootTest . Для этого нужно создать класс PostmanEchoClientTests . Установите для теста случайный порт с помощью аннотации @SpringBootTest .
@SpringBootTest(
webEnvironment = WebEnvironment.RANDOM_PORT)
class PostmanEchoClientTests {
}
Выполните автоматическое внедрение (autowire) созданного Spring bean-компонента PostmanEchoClient в класс модульного теста. Bean-компонент должен использовать настроенный список серверов Ribbon для балансировки нагрузки на клиентской стороне.
@Autowired private PostmanEchoClient client;
Создайте метод getEcho с аннотацией @Test , использующий внедренный bean-компонент PostmanEchoClient для вызова конечной точки GET-запроса Postman Echo.
После вызова конечной точки проверьте, совпадает ли возвращаемый ответ EchoGetResponse с переданными аргументами запроса, убедившись, что он правильно десериализован из ожидаемого содержимого JSON.
Добавление клиентского метода POST-запроса
Теперь, протестировав GET, давайте перейдем к запросу POST. Добавьте в интерфейс PostmanEchoClient клиентский метод postEcho , принимающий параметры запроса String foo и String bar . У него должно быть тело запроса объекта EchoPostRequest , а возвращаться им должен объект EchoPostResponse .
Добавьте к этому POST-запросу path /post при помощи аннотации @PostMapping . При вызове этот клиентский метод будет вызывать конечную точку POST-запроса Postman Echo.
Конечная точка будет повторять переданные параметры и body запроса в виде ответа в элементах args и data .
Создайте новый класс EchoPostRequest , который будет представлять тело JSON-запроса. EchoPostRequest — это класс простого объекта Java (POJO), содержащий одно свойство сообщения String . При вызове к конечной точке POST-запроса Postman Echo объект EchoPostRequest будет сериализован в тело JSON-запроса.
Создайте другой класс EchoPostResponse , который будет представлять ответ JSON. Этот класс содержит args со свойствами foo и bar , а также data со свойством message . Ответ JSON будет десериализован как EchoPostResponse в нашем клиентском методе Post-запроса Postman Echo.
Тестирование метода POST-запроса с помощью ‘SpringBootTest’
Снова включаем метод теста postEcho в ранее добавленный класс PostmanEchoClientTests для только что добавленного клиентского метода postEcho . Как и ранее используйте для вызова конечной точки POST-запроса Postman Echo автоматическое внедрение PostmanEchoClient .
После вызова удаленной точки убедитесь, что возвращаемый ответ EchoPostResponse совпадает с переданными аргументами и телом запроса, подтверждающими его верную десериализацию из ожидаемого формата JSON.
Интеграция с Eureka Server
С помощью простой аннотации и некоторой настройки можно быстро активировать в сервисе Spring REST клиента Netflix Eureka Discovery.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
Далее добавьте в класс application аннотацию @EnableDiscoveryClient , чтобы активировать реализацию клиента Netflix Eureka Discovery. Затем он зарегистрируется в реестре сервисов Netflix Eureka Server и будет использовать абстракцию Spring Cloud DiscoveryClient для запроса метаданных, содержащих конечные точки сервисов, которые клиент Ribbon будет использовать для балансировки нагрузки.
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class Application {
public static final void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
}
И наконец, укажите имя приложения и URL конечной точки Eureka Server в application.yaml .
Клиент Eureka использует имя приложения для регистрации на Eureka Server. Если это имя не указать, ваш сервис будет отображаться на Eureka Server как неизвестный.
spring:
application:
name: feignclient
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
Проделав эти несколько шагов, вы интегрировали сервис Spring REST в Eureka Server.
Теперь, когда ваш REST-сервис готов к подключению к Eureka Server, нужно удалить свойство listOfServers для Ribbon из application.yaml или закомментировать его.
# postmanEcho:
# ribbon:
# listOfServers: https://postman-echo.com/
Создание Eureka Server
Создайте с помощью Spring Initializr другой проект — на этот раз для Eureka Server, добавив в него зависимость Eureka Server.
Если вы начинаете новый Maven-проект, импортируйте Spring Cloud Dependencies POM и добавьте зависимость Spring Cloud Starter Netflix Eureka Server.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
В качестве альтернативы можно загрузить проект Eureka Server с GitHub .
Вам нужно создать стандартный класс точки входа с аннотацией @SpringBootApplication . Для активации реализации Eureka Server также добавьте в него аннотацию @EnableEurekaServer .
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
public static final void main(final String[] args) {
SpringApplication.run(EurekaServer.class, args);
}
}
Далее измените порт сервера на 8761 .
server:
port: 8761
Наконец, деактивируйте в application.yaml функции саморегистрации и запроса реестра.
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
registerWithEureka: false
fetchRegistry: false
Теперь Eureka Server готов, можете запускать!
Поскольку Postman Echo является внешним сервисом, нужно вручную зарегистрировать его на Eureka Server. Список REST-операций, поддерживаемых Eureka, можно найти здесь . Этот API понадобится вам только для регистрации нового приложения.
Отправьте с помощью Postman запрос на Eureka Server.
URL для регистрации Postman Echo на Eureka Server следующий:
http://localhost:8761/eureka/apps/POSTMAN-ECHO
Запросите содержимое для регистрации Postman Echo на Eureka Server так:
Нужно зарегистрировать приложение как POSTMAN-ECHO , потому что ранее мы назвали @FeignClient как postman-echo .
В результате вы должны получить успешный ответ без содержимого.
Откройте панель Eureka, где должна отразиться регистрация POSTMAN-ECHO .
Еще раз запустите PostmanRestClientTests , чтобы убедиться, что все работает как надо.
Заключение
Мы научились создавать декларативного REST-клиента при помощи Spring Cloud OpenFeign, задействовав Spring Cloud Netflix Ribbon для обеспечения на клиентской стороне балансировки нагрузки и отказоустойчивости. Плюсом к этому мы узнали, как настраивать конечную точку статического сервера для Ribbon и как выполнять интеграцию с Eureka Server для получения списка конечных точек зарегистрированных серверов. Весь исходный код доступен на GitHub .
Благодарю за чтение и надеюсь, что статья оказалась для вас полезна.
Читайте также:
Перевод статьи Andy Lian : Building a REST Client with Spring Cloud OpenFeign and Netflix Ribbon