- Get link
- X
- Other Apps
- Get link
- X
- Other Apps
In English
Недавно на работе исследовал возможность разделения нашего монолитного приложения на сервисы (работаю Java разработчиком). Конечно же, в разумное время распилить наше бэкенд-приложение было невозможно, поэтому исследование было сконцентрировано на постепенном переносе монолитной архитектуры на сервисную. Это, конечно, тоже сложно, но уже не невозможно!
В результате яросного гугления (хихи) к своему удивлению я узнал, что гигант стриминга фильмов и сериалов из США, а именно Netflix, полностью открыл код своего микросервисного стека.
Вот что я узнал пока почитывал про все это: микросервисная архитектура отлично масштабируется и проще в отладке и изменении кода а также то, что в Интернете очень мало гайдов с примерами использования микросервисов Netflix на Spring Cloud без Eureka (которая ответственна за построение регистра сервисов).
Поэтому в этой серии постов я постараюсь пролить немного света на стек микросервисов от Netflix, начиная с Feign - библиотеки для создания декларативных REST-клиентов. За ним последует пост на горячую тему балансера нагрузки Ribbon (с хардкоднутым списком сервисов), и, надеюсь, еще хватит сил на третий пост о добавлении в эту связку динамического списка сервисов при помощи Eureka.
Если "создадим spring boot приложение" не звучит супер просто, то можно воспользоваться этим туториалом. С ним Вы научитесь этому делу буквально минут за 10.
Добавим зависимость от feign starter в наш pom.xml (секция <dependencies>):
Теперь нужно понять, где взять REST API из которого будем брать данные. Для полноты демонстрации в этом примере мы потестим все CRUD операции. Так где же взять такой API, который будет соответствовать нашим требованиям? Как ни странно, в существуют бесплатные веб-сайты которые делают именно то что нам надо: предоставляют простой REST API для тестирования. Я нашел 2 таких: ReqRes и JSONplaceholder:
Feign использует интерфейсы аннотированные @FeignClient чтобы генерировать API запросы и мапить ответ на Java классы. Давайте сначала составим список вызовов к выбранному нами REST API:
Теперь можно создать модели и описать вызовы REST API Feign'у:
Если Вам хочется посмотреть как работают другие методы (create, update, delete) и узнать как извлечь UserClient в бин вместо того чтобы создавать его каждый раз когда он понадобится, буду рад продолжить повествование😊
Представим себе настоящее приложение, в котором точно есть как минимум 50 классов. Скажем, около 20 из них будут использовать UserClient для аутентификации. Думаете, писать в каждом классе Feign.build().... - это боль? Тогда подумайте о вполне вероятном сценарии смене транспорта с JSON на XML! Вот здесь очень помогут замечательные бины. Прежде чем начать реализацию остальных CRUD методов, превратим UserClient в бин:
Как всегда, полный код проекта можно найти тут: GitHub
Недавно на работе исследовал возможность разделения нашего монолитного приложения на сервисы (работаю Java разработчиком). Конечно же, в разумное время распилить наше бэкенд-приложение было невозможно, поэтому исследование было сконцентрировано на постепенном переносе монолитной архитектуры на сервисную. Это, конечно, тоже сложно, но уже не невозможно!
В результате яросного гугления (хихи) к своему удивлению я узнал, что гигант стриминга фильмов и сериалов из США, а именно Netflix, полностью открыл код своего микросервисного стека.
Вот что я узнал пока почитывал про все это: микросервисная архитектура отлично масштабируется и проще в отладке и изменении кода а также то, что в Интернете очень мало гайдов с примерами использования микросервисов Netflix на Spring Cloud без Eureka (которая ответственна за построение регистра сервисов).
Поэтому в этой серии постов я постараюсь пролить немного света на стек микросервисов от Netflix, начиная с Feign - библиотеки для создания декларативных REST-клиентов. За ним последует пост на горячую тему балансера нагрузки Ribbon (с хардкоднутым списком сервисов), и, надеюсь, еще хватит сил на третий пост о добавлении в эту связку динамического списка сервисов при помощи Eureka.
Немного теории
Прежде чем начинать работать с полностью новой технологией, неплохо бы почитать на эту тему. Вот составленный мной список полезных ссылок по микросервисам на Netflix:- Spring Cloud guide (самое главное - секция Spring Cloud Netflix)
- Spring.io Getting Started: Client Side Load Balancing with Ribbon and Spring Cloud
- exampledriven: Spring Cloud, Eureka, Ribbon, Feign example
- Piotr's TechBlog Part 1: Creating microservice using Spring Cloud, Eureka and Zuul
- Rafael Salerno's blog: Spring Cloud Netflix - Load Balancer with Ribbon/Feign
Практика!
Начнем с того, что создадим для нашего REST клиента Spring Boot приложение. Это не то чтобы обязательно, но Boot упрощает конфигурацию и запуск, да и Spring Cloud Feign starter (что такое стартер?) которой мы будем использовать все равно подтягивает Spring Boot в качестве зависимости.Если "создадим spring boot приложение" не звучит супер просто, то можно воспользоваться этим туториалом. С ним Вы научитесь этому делу буквально минут за 10.
Добавим зависимость от feign starter в наш pom.xml (секция <dependencies>):
1 2 3 4 5 | < dependency > < groupid >org.springframework.cloud</ groupId > < artifactid >spring-cloud-starter-openfeign</ artifactId > < version >1.4.0.RELEASE</ version > </ dependency > |
Теперь нужно понять, где взять 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 самому.
Щелкните дважды чтобы выбрать весь код 1234567891011121314<
dependencies
>
...
<
dependency
>
<
groupid
>org.projectlombok</
groupId
>
<
artifactid
>lombok</
artifactId
>
<
version
>1.16.20</
version
>
</
dependency
>
<
dependency
>
<
groupId
>io.github.openfeign</
groupId
>
<
artifactId
>feign-jackson</
artifactId
>
<
version
>10.7.0</
version
>
</
dependency
>
...
</
dependencies
>
- Создадим модели в пакете model под корневым пакетом.
Щелкните дважды чтобы выбрать весь код 12345678910111213141516171819202122232425262728293031323334353637@Data
public
class
User {
Long id;
String name;
String username;
String email;
Address address;
String phone;
String website;
Company company;
}
@Data
public
class
Address {
String street;
String suite;
String city;
String zipcode;
Geo geo;
}
@Data
public
class
Geo {
String lat;
String lng;
}
@Data
public
class
Company {
String name;
String catchphrase;
String bs;
}
- Создадим интерфейс клиента Feign в пакете feign под корневым пакетом.
Щелкните дважды чтобы выбрать весь код 1234567891011121314151617public
interface
UserClient {
@RequestMapping
(method = RequestMethod.POST, value =
"/users"
)
User createUser(User user);
@RequestMapping
(method = RequestMethod.GET, value =
"/users"
)
List<user> getUsers();
@RequestMapping
(method = RequestMethod.GET, value =
"/users/{userId}"
)
User getUser(
@PathVariable
(
"userId"
) Long userId);
@RequestMapping
(method = RequestMethod.PUT, value =
"/users/{userId}"
)
User updateUser(
@PathVariable
(
"userId"
) Long userId, User user);
@RequestMapping
(method = RequestMethod.DELETE, value =
"/users/{userId}"
)
void
deleteUser(
@PathVariable
(
"userId"
) Long userId);
}
-
Создадим контроллер с вызовом клиента Feint, просто чтобы убедиться что все работает.
Детальное описание что делает каждая строка:
Щелкните дважды чтобы выбрать весь код 12345678910111213141516171819@Controller
public
class
UserController {
@RequestMapping
(
"/read"
)
@ResponseBody
String read() {
UserClient userClient = Feign.builder()
.contract(
new
SpringMvcContract())
.encoder(
new
JacksonEncoder())
.decoder(
new
JacksonDecoder())
.logger(
new
Logger.ErrorLogger())
.logLevel(Logger.Level.FULL)
List<user> users = userClient.getUsers();
return
String.format(
"Retrieved %d users total"
, users.size());
}
}
.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 коммуникаций:
Если Вам хочется посмотреть как работают другие методы (create, update, delete) и узнать как извлечь UserClient в бин вместо того чтобы создавать его каждый раз когда он понадобится, буду рад продолжить повествование😊
Представим себе настоящее приложение, в котором точно есть как минимум 50 классов. Скажем, около 20 из них будут использовать UserClient для аутентификации. Думаете, писать в каждом классе Feign.build().... - это боль? Тогда подумайте о вполне вероятном сценарии смене транспорта с JSON на XML! Вот здесь очень помогут замечательные бины. Прежде чем начать реализацию остальных CRUD методов, превратим UserClient в бин:
- Создадим класс с конфигурацией для контекста приложения Spring в пакете config под корневым пакетом.
Щелкните дважды чтобы выбрать весь код 1234567891011121314151617@Configuration
public
class
Config {
@Value
(
"${feign.userclient.url}"
)
String userClientUrl;
@Bean
UserClient userClient() {
return
Feign.builder()
.contract(
new
SpringMvcContract())
.encoder(
new
JacksonEncoder())
.decoder(
new
JacksonDecoder())
.logger(
new
Logger.ErrorLogger())
.logLevel(Logger.Level.FULL)
.target(UserClient.
class
, userClientUrl);
}
}
В каталоге /src/main/resources надо создать файл application.properties с одной-единственной строкой:
Щелкните дважды чтобы выбрать весь код 1feign.userclient.url = https://jsonplaceholder.typicode.com
-
Теперь UserClient можно автовайрить в любой класс. Теперь можно переписать контроллер, чтобы он включал в себя все CRUD операции и намного более простой автовайреный UserClient:
Щелкните дважды чтобы выбрать весь код 1234567891011121314151617181920212223242526272829303132333435363738394041424344@Controller
public
class
UserController {
@Autowired
UserClient userClient;
@RequestMapping
(
"/create"
)
@ResponseBody
String create() {
User user = userClient.getUser(1L);
user.setId(
null
);
user = userClient.createUser(user);
return
String.format(
"Created a user with id %d"
, user.getId());
}
@RequestMapping
(
"/read"
)
@ResponseBody
String read() {
List<user> users = userClient.getUsers();
return
String.format(
"Retrieved %d users total"
, users.size());
}
@RequestMapping
(
"/update"
)
@ResponseBody
String update() {
User user = userClient.getUser(1L);
String oldName = user.getName();
user.setName(
"John"
);
userClient.updateUser(1L, user);
return
String.format(
"Update successful. User id: %d, old name: %s, new name: %s"
,
user.getId(), oldName, user.getName());
}
@RequestMapping
(
"/delete"
)
@ResponseBody
String delete() {
userClient.deleteUser(1L);
return
"Deleted"
;
}
}
-
Если теперь приложение запустилось без ошибок, то автовайринг уже творит свою магию. Наконец, можно опробовать каждый метод при помощи браузера:
Как всегда, полный код проекта можно найти тут: GitHub
Comments
"...и намного более простой автовайреный UserService..." так мы вроде внедряем UserClient ?
ReplyDeleteСпасибо! Исправлю.
DeleteЗдравствуйте,
ReplyDeletecom.netflix.feign в поледний раз обновлялся Jul 14, 2016
лучше взять от github:
https://mvnrepository.com/artifact/io.github.openfeign/feign-jackson
Благодарю! Я так понимаю, вы будете пробовать запускать с github? Напишите, получится или нет, если будет время.
DeleteУже было испробовано на момент написания комментария - отлично работает (пробовал с vk.api). Даже код в примере менять не нужно, только зависимость.
ReplyDeleteСпасибо за статью :)
Наконец нашел время обновить зависимость в посте!
DeleteБольшое спасибо за такое прекрасное объяснение! Сразу всё понятно, и написано по человечески, без непонятной мути
ReplyDelete;-)
DeleteА почему используется Feign.builder() вместо @FeignClient(...) к интерфейсу ?
ReplyDeleteУже не помню, но полагаю с аннотацией будет даже лучше.
Delete