Статьи

Микросервисы Spring Boot, мастерская Docker и Kubernetes — часть 2

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