В предыдущем посте мы создали наш первый микро сервис «ProductService», используя SpringBoot и Docker. В этой части мы подробно рассмотрим, как управлять несколькими микросервисами с помощью Spring Cloud, библиотек netflix, шлюзов API.
Для нашей системы управления заказами, скажем, минимальные отношения могут быть примерно такими:
Итак, давайте создадим еще 2 сервиса под названием «orderService» и «customerService» таким же образом, как мы создаем «productService».
OrderService
Чтобы создать заказ, мы могли бы передать customerId, список товаров с productIds и количеством. Давайте посмотрим, как это сделать:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@PostMapping ( "/orders" ) public Order save( @RequestBody CustomerOrderRequest request) { return orderRepository.save(Order .builder() .customerId(request.getCustomerId()) .externalReference(request.getExternalReference()) .items(toItems(request.getItems())).build()); } private List toItems(List items) { return items.stream().map(item -> Item.builder().productId(item.getProductId()) .quantity(item.getQuantity()).build()).collect(Collectors.toList()); } |
Здесь мы сохраняем customerId, список товаров с productIds в базе данных.
Для получения полной информации о заказе нам понадобится полный объект клиента и информация о продукте. Результат будет выглядеть примерно так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
{ "orderId" : "1234" , "externalReference" : "234257hf" , "customer" : { "id" : 123, "firstName" : "anirudh" , "lastName" : "bhatnagar" , "phone" : "21323" , "address" : { "addressLine1" : "123" , "addressLine2" : "pwe" , "city" : "Syd" , "state" : "NSW" , "country" : "Aus" , "postcode" : 2000 } }, "createdDate" : "2018-11-12" , "items" : [{ "product" : { "id" : 123, "name" : "Nike Shoes" , "description" : "Mens shoes" , "price" : "100" , "sku" : "1234" }, "quantity" : 3 }], "totalOrderCost" : "300.00" , "totalOrderTax" : "30.00" } |
Подробный ответ на заказ должен содержать подробную информацию о клиенте, адресе, продукте и общей стоимости заказа. Чтобы получить эту информацию, службе заказа необходимо будет получить подробности из службы продукта и службы поддержки.
Получение сведений о продукте из ProductService в сервисе заказа
Чтобы получить сведения о сервисе продукта в сервисе Order, нам понадобится работающий сервис продукта и клиент в orderController, чтобы выполнить HTTP-вызов GET для ProductService. Для httpClient мы бы использовали OpenFeign клиентскую библиотеку от Netflix, она доступна как часть весеннего начального облака. Итак, давайте добавим эту зависимость в наш файл build.gradle:
1
2
3
4
5
6
|
implementation( 'org.springframework.cloud:spring-cloud-starter-openfeign' ) dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } } |
Теперь, когда мы добавили зависимость, мы будем создавать прокси-интерфейс с именем «ProductServiceProxy» для этого сервиса, используя @FeignClient:
1
2
3
4
5
6
|
@FeignClient (name = "product-service" , url = "localhost:8001" ) public interface ProductServiceProxy { @GetMapping ( "/products/{id}" ) Product getProduct( @PathVariable ( "id" ) Long id); } |
Мы добавили аннотацию @FeignClient к интерфейсу и настроили имя и URL-адрес службы продукта.
Нам также нужно включить клиент Feign для нашего приложения, добавив еще одну аннотацию в наш основной класс:
1
2
3
4
|
@SpringBootApplication @EnableFeignClients public class OrderServiceApplication { ...... |
Наконец, нам нужно выполнить вызов службы продукта, работающей на локальном порте 8001, чтобы получить сведения о продукте, используя идентификатор продукта, предоставленный в заказе, и заполнить объект ответа о деталях заказа:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@GetMapping ( "/orders/{id}" ) public CustomerOrderDetails getOrders( @PathVariable ( "id" ) Long orderId) { final Order order = orderRepository.findById(orderId).orElse( null ); if (order == null ) { return null ; } return toCustomerOrderDetails(order); } private CustomerOrderDetails toCustomerOrderDetails(Order order) { return CustomerOrderDetails.builder() .orderId(order.getId()) .createdDate(order.getCreatedDate()) .externalReference(order.getExternalReference()) .items(toItemList(order.getItems())) .build(); } private List<com.anirudhbhatnagar.orderService.dto.product.Item> toItemList(List<Item> items) { return items.stream().map(item -> toItemDto(item)).collect(Collectors.toList()); } private com.anirudhbhatnagar.orderService.dto.product.Item toItemDto(Item item) { return com.anirudhbhatnagar.orderService.dto.product.Item .builder() .product(productServiceProxy.getProduct(item.getProductId())).build(); } |
Если вы внимательно посмотрите на приведенный выше код,
1
|
productServiceProxy.getProduct(item.getProductId()) |
вы увидите, что, как только мы получим запрос на получение сведений о заказе для заданного идентификатора заказа, мы сначала получим данные заказа, сохраненные в базе данных службы заказов, а затем, используя productIds, предоставленные в каждом элементе или заказе, вызовем productService и заполним объект ответа orderDetails.
Попробуй это
После того, как orderService запущен и работает на порту 8002, а productService работает на порте 8001. Мы можем протестировать наше приложение: Убедитесь, что некоторые продукты созданы с использованием службы продукта, как описано в предыдущем блоге .
Запишите идентификатор продукта, который вы создали в службе продукта, и давайте создадим новый заказ, используя то же самое: Выполните POST на http: // localhost: 8002 / orders, используя почтальон, с запросом, как указано ниже:
1
2
3
4
5
6
7
8
|
{ "customerId" : "123" , "externalReference" : "1234567" , "items" : [{ "productId" : 1, "quantity" : 2 }] } |
Это создаст новый заказ, а не откликнется, чтобы найти идентификатор заказа. Теперь давайте извлечем детали заказа, используя этот идентификатор заказа: Сделайте GET на http: // localhost / 8002 / orders / {order-id}, это должно вернуть вам следующий ответ:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
{ "orderId" : 12, "externalReference" : "1234567" , "customer" : null , "createdDate" : null , "items" : [ { "product" : { "id" : "1" , "name" : "Nike" , "description" : "Shoes" , "price" : "100" , "sku" : "1234" }, "quantity" : 2 } ], "totalOrderCost" : "200" } |
Итак, здесь мы увидели, как служба заказа сделала запрос к услуге продукта и заполнила объект ответа. Тем не менее, мы по-прежнему считаем клиента «нулевым», поэтому для того, чтобы заполнить данные о клиенте, нам необходимо получить его из службы поддержки. Для того, чтобы настроить обслуживание клиентов, мы должны сделать следующее:
1. Настройте службу поддержки клиентов так же, как мы это сделали для продукта или службы заказа, используя Spring инициализатор.
2. Настройте службу прокси-клиента в OrderService
3. Вызовите CustomerService из диспетчера заказов, чтобы заполнить данные клиента внутри объекта ответа «Сведения о заказе».
Если все работает нормально, мы должны увидеть детали клиента.
В настоящее время мы жестко закодировали URL-адреса сервисов в сервисе заказов, но в идеале их нужно было бы динамически обнаруживать. Итак, в следующем разделе мы добавим «Обнаружение служб» и «Балансировка нагрузки» к нашим 3 микросервисам.
Весь исходный код может быть указан здесь .
Опубликовано на Java Code Geeks с разрешения Анирудх Бхатнагар, партнера нашей программы JCG. Смотреть оригинальную статью здесь: Spring Boot Microservices, Docker и Kubernetes мастерская — часть 2
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |