Nocturnal solar-powered lamp from a mason jar

Микросервисы с Netflix, часть 1: Feign

In English

Недавно на работе исследовал возможность разделения нашего монолитного приложения на сервисы (работаю Java разработчиком). Конечно же, в разумное время распилить наше бэкенд-приложение было невозможно, поэтому исследование было сконцентрировано на постепенном переносе монолитной архитектуры на сервисную. Это, конечно, тоже сложно, но уже не невозможно!

В результате яросного гугления (хихи) к своему удивлению я узнал, что гигант стриминга фильмов и сериалов из США, а именно Netflix, полностью открыл код своего микросервисного стека.

Вот что я узнал пока почитывал про все это: микросервисная архитектура отлично масштабируется и проще в отладке и изменении кода а также то, что в Интернете очень мало гайдов с примерами использования микросервисов Netflix на Spring Cloud без Eureka (которая ответственна за построение регистра сервисов).

Поэтому в этой серии постов я постараюсь пролить немного света на стек микросервисов от Netflix, начиная с Feign - библиотеки для создания декларативных REST-клиентов. За ним последует пост на горячую тему балансера нагрузки  Ribbon (с хардкоднутым списком сервисов), и, надеюсь, еще хватит сил на третий пост о добавлении в эту связку динамического списка сервисов при помощи Eureka.

Немного теории

Прежде чем начинать работать с полностью новой технологией, неплохо бы почитать на эту тему. Вот составленный мной список полезных ссылок по микросервисам на Netflix:
Feign - это первый шаг в реализации архитектуры микросервиса тулами Netflix. В реальности слабо связанных сервисов очень важно чтобы общение между ними было легковесным и простым для отладки. Поэтому для этой цели зачастую используют REST, хотя для некоторых случаев это может быть не лучшим выбором (смотрим, например, дискуссии на эту тему тут и тут). Для упрощения связи по REST мы и используем Feign: при помощи него мы будем поглощать сообщения от других сервисов и автоматически превращать их в Java объекты.

Практика!

Начнем с того, что создадим для нашего REST клиента Spring Boot приложение. Это не то чтобы обязательно, но Boot упрощает конфигурацию и запуск, да и Spring Cloud Feign starter (что такое стартер?) которой мы будем использовать все равно подтягивает Spring Boot в качестве зависимости.

Если "создадим spring boot приложение" не звучит супер просто, то можно воспользоваться этим туториалом. С ним Вы научитесь этому делу буквально минут за 10.

Добавим зависимость от feign starter в наш pom.xml (секция <dependencies>):

Теперь нужно понять, где взять REST API из которого будем брать данные. Для полноты демонстрации в этом примере мы потестим все CRUD операции. Так где же взять такой API, который будет соответствовать нашим требованиям? Как ни странно, в существуют бесплатные веб-сайты которые делают именно то что нам надо: предоставляют простой REST API для тестирования. Я нашел 2 таких: ReqRes и JSONplaceholder:

ReqRes и JSONplaceholder предоставляют полноценный REST API чтобы мы могли потестить наше Feign приложение.

Feign использует интерфейсы аннотированные @FeignClient чтобы генерировать API запросы и мапить ответ на Java классы. Давайте сначала составим список вызовов к выбранному нами REST API:

Действие HTTP метод URL
(C) Создать пользователя POST https://jsonplaceholder.typicode.com/users
(R) Получить список пользователей GET https://jsonplaceholder.typicode.com/users
(R)Достать одного пользователя GET https://jsonplaceholder.typicode.com/users/{id}
(U) Обновить детали пользователя PUT or PATCH https://jsonplaceholder.typicode.com/users/{id}
(D) Удалить пользователя DELETE https://jsonplaceholder.typicode.com/users/{id}

Теперь можно создать модели и описать вызовы REST API Feign'у:
  • Добавить в список зависимостей Jackson - сериализатор/десериализатор JSON. Feint будет использовать его для HTTP запросов и ответов.
    (Опционально) Добавить в список зависимостей Lombok.
    Lombok автоматически генерирует стандартные геттеры/сеттеры/конструкторы для POJO. Можно не добавлять, но тогда придется писать весь boilerplate самому.
  • Создадим модели в пакете model под корневым пакетом.
  • Создадим интерфейс клиента Feign  в пакете feign под корневым пакетом.
  • Создадим контроллер с вызовом клиента Feint, просто чтобы убедиться что все работает. Детальное описание что делает каждая строка:

    .contract определяет какими аннотациями мы будем метить Feign интерфейсы. А именно, благодаря SpringMvcContract() можно использовать спринговые аннотации @RequestMapping. Без этого получим UnsatisfiedDependencyException: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userClient';
    .encoder и .decoder заставят Feint клент мапить JSON в Java объекты и обратно,
    .logger и .logLevel - логировать каждый запрос и ответ в System.error. Очень удобно для операций create, update и delete - чтобы понять, сработали они или тихо зафейлились,
    .target - в общем и так понятно. Сюда наше приложение будет слать запросы и получать ответы.

  • Запустите получившееся приложение. Теперь заходим на http://localhost:8080/read.
    Сработало! Получается соединиться и получить список юзеров. Можно также обратиться в консоль или окно консоли в IDE за деталями HTTP  коммуникаций:
Это был абсолютный минимум который надо проделать, чтобы попробовать Feint в Spring Cloud.

Если Вам хочется посмотреть как работают другие методы (create, update, delete) и узнать как извлечь UserClient в бин вместо того чтобы создавать его каждый раз когда он понадобится, буду рад продолжить повествование😊

Представим себе настоящее приложение, в котором точно есть как минимум 50 классов. Скажем, около 20 из них будут использовать UserClient для аутентификации. Думаете, писать в каждом классе Feign.build().... - это боль? Тогда подумайте о вполне вероятном сценарии смене транспорта с  JSON на XML! Вот здесь очень помогут замечательные бины. Прежде чем начать реализацию остальных CRUD методов, превратим UserService в бин:
  • Создадим класс с конфигурацией для контекста приложения Spring в пакете config под корневым пакетом.

    В каталоге /src/main/resources надо создать файл application.properties с одной-единственной строкой:
  • Теперь UserService можно автовайрить в любой класс. Теперь можно переписать контроллер, чтобы он включал в себя все CRUD операции и намного более простой автовайреный UserService:
  • Если теперь приложение запустилось без ошибок, то автовайринг уже творит свою магию. Наконец, можно опробовать каждый метод при помощи браузера:

Спасибо за то что дочитали до конца! Скоро появится часть 2 этого поста, где мы добавим балансировщик нагрузки Ribbon с захардкоженным списом сервисов!

Как всегда, полный код проекта можно найти тут: GitHub

Comments