.. | ||
order-service | ||
product-service | ||
database.sql | ||
docker-compose.yaml | ||
nginx.conf | ||
pic1.png | ||
pic2.png | ||
README.md |
Лабораторная работа 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
- указывает порт для прослушивания запрсов - 80server_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
и назначить её каждому из контейнеров.
Разворачивание микросервисного приложения
Пример запроса к микросервисному приложению:
Примеры остальных запросов показаны в видео.