В предыдущем посте мы создали наш первый микро сервис «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", "email": "test@test.com", "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@EnableFeignClientspublic 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, являются их собственными. |
