DAS_2023_1/arutunyan_dmitry_lab_3
2023-11-12 20:59:21 +04:00
..
order-service arutunyan_dmitry_lab_3 is ready 2023-11-12 20:59:21 +04:00
product-service arutunyan_dmitry_lab_3 is ready 2023-11-12 20:59:21 +04:00
database.sql arutunyan_dmitry_lab_3 is ready 2023-11-12 20:59:21 +04:00
docker-compose.yaml arutunyan_dmitry_lab_3 is ready 2023-11-12 20:59:21 +04:00
nginx.conf arutunyan_dmitry_lab_3 is ready 2023-11-12 20:59:21 +04:00
pic1.png arutunyan_dmitry_lab_3 is ready 2023-11-12 20:59:21 +04:00
pic2.png arutunyan_dmitry_lab_3 is ready 2023-11-12 20:59:21 +04:00
README.md arutunyan_dmitry_lab_3 is ready 2023-11-12 20:59:21 +04:00

Лабораторная работа 3. Вариант 4.

Задание

  • Создать 2 микросервиса, реализующих CRUD на связанных сущностях.
  • Реализовать механизм синхронного обмена сообщениями между микросервисами.
  • Реализовать шлюз на основе прозрачного прокси-сервера nginx. Описание предметной области:

Микросервисное приложение "Онлайн магазин", состоящее из 4х сервисов:

  • СУБД PostgreSQL - используется в качестве хранилища данных приложения, содержит в себе БД с сущностями "заказ" и "товар",
  • Микросервис "Товар" - микросервисное приложение, отвечающее за CRUD сущности "товар",
  • Микросервис "Заказ" - микросервисное приложение, отвечающее за CRUD сущности "заказ" и её связи с сущностью "товар",
  • Прокси-сервер nginx - используется в качестве шлюза для переадресации запросов на конкретные микросервисы.

Как запустить

В директории с файлом характеристик docker-compose.yaml выполнить команду:

docker-compose -f docker-compose.yaml up

Это запустит docker-compose, который развернёт в общем контейнере 4 контейнера с сервисами для работы микросервисного приложения.

Описание работы

Разработка сервиса БД

Согласно описанию предметной области, БД приложения будет состоять из 2х сущностей, связанных как "Один ко многим":

order (single) -> product (many)

В качестве СУБД будет использована PostgreSQL. Для создания таблиц базы данных, создания связей между ними их заполнения тестовыми данными создадим файл sql-запросов database.sql:

-- Создание таблицы заказов
CREATE TABLE t_order (
                     id INTEGER PRIMARY KEY UNIQUE NOT NULL,
                     status VARCHAR(255) NOT NULL
);
-- Создание таблицы товаров
CREATE TABLE t_product (
                     id INTEGER PRIMARY KEY UNIQUE NOT NULL,
                     name VARCHAR(255) NOT NULL,
                     price INTEGER NOT NULL,
                     order_id INTEGER,
                     FOREIGN KEY (order_id) REFERENCES t_order(id)
);
-- Добавление пустых заказов
INSERT INTO t_order (id, status)
VALUES (0, 'Принят'),
       (1, 'В обработке');

-- Добавление товаров
INSERT INTO t_product (id, name, price)
VALUES (0, 'Гантели', 5000),
       (1, 'Эспандер', 500),
       (2, 'Тренажёр', 25990);

Для разворачивания сервиса БД в контейнере, пропишем необходимые конфигурации в файле docker-compose.yaml:

db-market-api:
    image: postgres:latest
    ports:
      - 5432:5432
    environment:
      POSTGRES_PASSWORD: admin
      POSTGRES_USER: admin
      POSTGRES_DB: market-api
    volumes:
      - ./database.sql:/docker-entrypoint-initdb.d/database.sql
    restart: always

Где image указывает, что мы берём последний образ postgres, ports указывает запускать сервис на порту 5432, environment определяет название БД - market-api, а volumes указывает, что при запуске контейнера и вызове функции initdb, необходимо использовать созданный ранее файл database.sql.

Разработка микросервиса "Товар"

Микросервис "Товар" представляет собой SpringBoot приложение на языке Java. Приложение было разработано в соответствии со стандартом реализации CRUD с использованием JPA.

На выходе у микросервиса лежит rest интерфейс swagger, через который можно отправить CRUD запросы приложению.

Чтобы настроить связь приложения и БД, создадим конфигурационный файл application.yaml:

server:
  port: "8080"
spring:
  datasource:
    url: jdbc:postgresql://db-market-api:5432/market-api
    username: admin
    password: admin

Где port указывает, что микросервис будет развёрнут на порту 8080, а datasource/url указывает адрес к сервису с БД market-api.

Чтобы создать образ микросервиса для docker контейнера, создадим Dockerfile:

FROM openjdk:17-jdk

WORKDIR /app
COPY ./product-service/build/libs/product-service-0.0.1-SNAPSHOT.jar /app/product-service-0.0.1-SNAPSHOT.jar
EXPOSE 8080

CMD ["java", "-jar", "product-service-0.0.1-SNAPSHOT.jar"]

В которм указывается версия jdk для запуска микросервиса, копируется исполняемый файл приложения, указывается порт и обозначается команда при старте контейнера: java -jar product-service-0.0.1-SNAPSHOT.jar

Чтобы развернуть микросервис в контейнере, пропишем его конфигурации в файле docker-compose.yaml:

product-service:
  build:
    context: .
    dockerfile: ./product-service/Dockerfile
  ports:
    - 8080:8080
  restart: always
  depends_on:
    - db-market-api

Где build указывает на метод сборки образа в котором: context указывает на корневую директорию, а dockerfile указывает на путь к Dockerfile, ports устанавливает порт для обращения к микросервису 8080, depends_on указывает, что микросервис зависим от БД и разворачивается только после запуска postgres.

Разработка микросервиса "Заказ"

Микросервис "Заказ" представляет собой SpringBoot приложение на языке Java. Приложение было разработано в соответствии со стандартом реализации CRUD с использованием JPA. В отличие от микросервиса "Товар", данный микросервис отвечает также за связь сущностей товара и заказа, поэтому в соответсвующих методах он отправляет запрос к сервису товаров.

Метод добавления товара в заказ:

RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> entity = new HttpEntity<>(new HttpHeaders());
ResponseEntity<String> response = restTemplate.exchange(
    "http://product-service:8080/product/" + product_id, HttpMethod.GET, entity, String.class);

if (response.getStatusCode().is2xxSuccessful()) {
    String responseBody = response.getBody();
    ObjectMapper objectMapper = new ObjectMapper();
    ProductDto productDto;
    try {
        productDto = objectMapper.readValue(responseBody, ProductDto.class);
    } catch (JsonProcessingException e) {
        throw new Exception("Не удалось десериализовать тело запроса в объект product");
    }
    productDto.setOrder_id(order_id);
    restTemplate.exchange("http://product-service:8080/product/", HttpMethod.PUT, new HttpEntity<>(productDto), String.class);
} else {
    throw new Exception("Ошибка получения объекта product");
}

Таким же способом запросов к микросервису товаров работают методы чтения всех заказов и чтения одного заказа. Этим и достигается микросервисная архитектура приложения.

На выходе у микросервиса лежит rest интерфейс swagger, через который можно отправить CRUD запросы приложению.

Чтобы настроить связь приложения и БД, создадим конфигурационный файл application.yaml:

server:
  port: "8081"
spring:
  datasource:
    url: jdbc:postgresql://db-market-api:5432/market-api
    username: admin
    password: admin

Где port указывает, что микросервис будет развёрнут на порту 8081, а datasource/url указывает адрес к сервису с БД market-api.

Чтобы создать образ микросервиса для docker контейнера, создадим Dockerfile:

FROM openjdk:17-jdk

WORKDIR /app
COPY ./order-service/build/libs/order-service-0.0.1-SNAPSHOT.jar /app/order-service-0.0.1-SNAPSHOT.jar
EXPOSE 8081

CMD ["java", "-jar", "order-service-0.0.1-SNAPSHOT.jar"]

В которм указывается версия jdk для запуска микросервиса, копируется исполняемый файл приложения, указывается порт и обозначается команда при старте контейнера: order-service-0.0.1-SNAPSHOT.jar

Чтобы развернуть микросервис в контейнере, пропишем его конфигурации в файле docker-compose.yaml:

order-service:
  build:
    context: .
    dockerfile: ./order-service/Dockerfile
  ports:
    - 8081:8081
  restart: always
  depends_on:
    - db-market-api

Где build указывает на метод сборки образа в котором: context указывает на корневую директорию, а dockerfile указывает на путь к Dockerfile, ports устанавливает порт для обращения к микросервису 8081, depends_on указывает, что микросервис зависим от БД и разворачивается только после запуска postgres.

Настройка прокси-сервера Nginx

Согласно заданию, в данной архитектуре nginx выполняет работу шлюза и переадресовывает запросы на микросервисы в зависимости от их заголовков. Чтобы это осуществить, пропишем файл конфигураций nginx:

events {
    worker_connections  1024;
}

http {
    upstream product-service {
        server product-service:8080;
    }
    upstream order-service {
        server order-service:8081;
    }
    server {
        listen 80;
        listen      [::]:80;
        server_name localhost;
        location /product-service/ {
            proxy_pass http://product-service/;
        }
        location /order-service/ {
            proxy_pass http://order-service/;
        }
    }
}

Где events создаёт событие запуска nginx, upstream product-service создаёт поток запросов по адресу микросервиса товаров product-service:8080, upstream order-service создаёт поток запросов по адресу микросервиса заказов order-service:8080, server разворачивает сервер-шлюз, в котором:

  • listen - указывает порт для прослушивания запрсов - 80
  • server_name - указывает host сервера, по которому будут проходить запросы - localhost
  • location /product-service/ - указывает префикс запросов к микросервису товаров
    • proxy_pass http://product-service/ - адресует запросы с данным префиксом к микросервису товаров
  • location /order-service/ - указывает префикс запросов к микросервису заказов
    • proxy_pass http://order-service/ - адресует запросы с данным префиксом к микросервису заказов

Чтобы развернуть прокси-сервер nginx в контейнере, пропишем его конфигурации в файле docker-compose.yaml:

nginx:
  image: nginx:latest
  ports:
    - 80:80
  volumes:
    - ./nginx.conf:/etc/nginx/nginx.conf
  restart: always
  depends_on:
    - product-service
    - order-service

Где image указывает, что мы берём последний образ nginx, ports указывает запускать сервер на порту 80, volumes указывает, что при запуске контейнера, локальный файл конфигураций nginx.conf следует поместить вместо стандартного файла конфигураций nginx, а depends_on указывает, что данный прокси-сервер зависим от обеих микросервисов и разворачивается только после их запуска.

Note

Для обеспечения работы прокси-сервера nginx в качестве шлюза, необходимо чтобы все управляемые им сервисы находились в одной сети типа "мост". Если это не так, то в файле docker-compose.yaml необходимо создать виртуальную сеть между контейнерами

networks:
  mynetwork:
    driver: bridge

и назначить её каждому из контейнеров.

Разворачивание микросервисного приложения

log-журнал контейнеров:

Пример запроса к микросервисному приложению:

Примеры остальных запросов показаны в видео.

Видео

https://youtu.be/VxaZRfyu3pI