tabeev_alexander_lab_3 is ready

This commit is contained in:
2025-12-12 17:47:39 +04:00
parent 716d205ebb
commit 82949cf2b1
9 changed files with 291 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
services:
researchers:
build: ./researchers-service
container_name: researchers_service
ports:
- "5001:5000"
networks:
- app-network
publications:
build: ./publications-service
container_name: publications_service
ports:
- "5002:5000"
networks:
- app-network
gateway:
image: nginx:alpine
container_name: api_gateway
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- researchers
- publications
networks:
- app-network
networks:
app-network:
driver: bridge

View File

@@ -0,0 +1,34 @@
events {
worker_connections 1024;
}
http {
upstream researchers {
server researchers:5000;
}
upstream publications {
server publications:5000;
}
server {
listen 80;
location /researchers {
rewrite ^/researchers/(.*)$ /$1 break;
proxy_pass http://researchers;
proxy_set_header Host $host;
}
location /publications {
rewrite ^/publications/(.*)$ /$1 break;
proxy_pass http://publications;
proxy_set_header Host $host;
}
location / {
return 200 "API Gateway\n";
add_header Content-Type text/plain;
}
}
}

View File

@@ -0,0 +1,10 @@
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "5000"]

View File

@@ -0,0 +1,91 @@
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uuid
import requests
app = FastAPI(title="Publications Service")
publications_db = []
class PublicationCreate(BaseModel):
title: str
type: str
year: int
journal: Optional[str] = None
researcherUuid: str
class ResearcherInfo(BaseModel):
fullName: str
position: str
department: str
hIndex: int
class PublicationResponse(BaseModel):
uuid: str
title: str
type: str
year: int
journal: Optional[str] = None
researcherUuid: str
class PublicationDetailResponse(PublicationResponse):
researcherInfo: Optional[ResearcherInfo] = None
def get_researcher_info(researcher_uuid: str) -> Optional[ResearcherInfo]:
try:
response = requests.get(f"http://researchers:5000/researchers/{researcher_uuid}")
if response.status_code == 200:
return response.json()
return None
except requests.exceptions.RequestException:
return None
@app.get("/publications/", response_model=List[PublicationResponse])
def get_publications():
return publications_db
@app.get("/publications/{publication_uuid}", response_model=PublicationDetailResponse)
def get_publication(publication_uuid: str):
for publication in publications_db:
if publication["uuid"] == publication_uuid:
researcher_info = get_researcher_info(publication["researcherUuid"])
publication_detail = publication.copy()
publication_detail["researcherInfo"] = researcher_info
return publication_detail
raise HTTPException(status_code=404, detail="Publication not found")
@app.post("/publications/", response_model=PublicationResponse)
def create_publication(publication: PublicationCreate):
researcher_info = get_researcher_info(publication.researcherUuid)
if not researcher_info:
raise HTTPException(status_code=400, detail="Researcher not found")
publication_data = publication.dict()
publication_data["uuid"] = str(uuid.uuid4())
publications_db.append(publication_data)
return publication_data
@app.put("/publications/{publication_uuid}", response_model=PublicationResponse)
def update_publication(publication_uuid: str, publication_update: PublicationCreate):
researcher_info = get_researcher_info(publication_update.researcherUuid)
if not researcher_info:
raise HTTPException(status_code=400, detail="Researcher not found")
for publication in publications_db:
if publication["uuid"] == publication_uuid:
publication.update(publication_update.dict())
return publication
raise HTTPException(status_code=404, detail="Publication not found")
@app.delete("/publications/{publication_uuid}")
def delete_publication(publication_uuid: str):
for i, publication in enumerate(publications_db):
if publication["uuid"] == publication_uuid:
publications_db.pop(i)
return {"message": "Publication deleted successfully"}
raise HTTPException(status_code=404, detail="Publication not found")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5000)

View File

@@ -0,0 +1,4 @@
fastapi==0.104.1
uvicorn==0.24.0
pydantic==2.5.0
requests==2.31.0

View File

@@ -0,0 +1,45 @@
## Цель ЛР: изучение шаблона проектирования gateway, построения синхронного обмена между микросервисами и архитектурного стиля RESTful API.
## Задачи:
1. Создать 2 микросервиса, реализующих CRUD на связанных сущностях.
2. Реализовать механизм синхронного обмена сообщениями между микросервисами.
3. Реализовать шлюз на основе прозрачного прокси-сервера nginx.
## Предметная область
Тема диплома звучит так: Сбор и аналитика по научной активности : формирование анкеты по сотрудникам, подбор конференций, журналов, соавторов, грантов.
Исходя из темы добавил две сущности: учёные и научные публикации; связь у них один ко многим: у одного ученого может быть много публикаций и каждая публикация принадлежит только одному ученому(не знаю есть ли у ученых какая-то совместная публикация, но у меня вот так)
## Как запустить ЛР
1. Предварительно должны быть установлены Docker и Docker Compose и свободные порты
2. Перейти в директорию с файлом docker-compose.yml
3. Запустить сервисы с помощью команды docker-compose up --build / down - для отключения
4. Убедиться что в докере везде статус Running
5. Перейтии по адресу http://localhost:5001/docs#/ (ученые) и http://localhost:5002/docs#/ (научные публикации)
## Основные технологии
1. FastAPI - веб-фреймворк для создания REST API на Python
2. Docker - контейнеризация микросервисов
3. Docker Compose - сбор контейнеров
4. NGINX - веб-сервер и API Gateway
5. Python 3.9 - ЯП для реализации логики
### Что делает ЛР
Есть два микросервиса:
1. микросервис Researchers Service - сущность ученые с методами 2 get(один для получения всего списка ученых, а второй для получения конкретного ученого по uuid), post, put, delete
2. микросервис Publications Service - суущность научные публикации с такими же метордами как у ученых, но есть привязка публикации к ученому, указав его uuid
## Сценарий
1. Запрашиваем информацию о научной публикации, указав uuid публикации
2. Publications Service делает http-запрос к Reserchers Service
3. Получает информацию об авторе публикации
4. Объединяет данные и возвращает полный ответ
## Вывод
В ходе ЛР успешно разработана микросервисная архитектура, состоящая из двух взаимосвязанных сервисов. Реализовано:
1. Полный crud для ученых и научных публикаций
2. Синхронный http-обмен между сервисаами
3. API Gateway на основе nginx для централизованного управдления запросами
4. Контейнеризация с помощью докера
## Ссылка на видео: https://vkvideo.ru/video-234192775_456239019?list=ln-epmNwqwISb1eF2lbtc

View File

@@ -0,0 +1,10 @@
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "5000"]

View File

@@ -0,0 +1,60 @@
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uuid
from datetime import datetime
app = FastAPI(title="Researchers Service")
researchers_db = []
class ResearcherCreate(BaseModel):
fullName: str
position: str
department: str
hIndex: int
class ResearcherResponse(BaseModel):
uuid: str
fullName: str
position: str
department: str
hIndex: int
@app.get("/researchers/", response_model=List[ResearcherResponse])
def get_researchers():
return researchers_db
@app.get("/researchers/{researcher_uuid}", response_model=ResearcherResponse)
def get_researcher(researcher_uuid: str):
for researcher in researchers_db:
if researcher["uuid"] == researcher_uuid:
return researcher
raise HTTPException(status_code=404, detail="Researcher not found")
@app.post("/researchers/", response_model=ResearcherResponse)
def create_researcher(researcher: ResearcherCreate):
researcher_data = researcher.dict()
researcher_data["uuid"] = str(uuid.uuid4())
researchers_db.append(researcher_data)
return researcher_data
@app.put("/researchers/{researcher_uuid}", response_model=ResearcherResponse)
def update_researcher(researcher_uuid: str, researcher_update: ResearcherCreate):
for researcher in researchers_db:
if researcher["uuid"] == researcher_uuid:
researcher.update(researcher_update.dict())
return researcher
raise HTTPException(status_code=404, detail="Researcher not found")
@app.delete("/researchers/{researcher_uuid}")
def delete_researcher(researcher_uuid: str):
for i, researcher in enumerate(researchers_db):
if researcher["uuid"] == researcher_uuid:
researchers_db.pop(i)
return {"message": "Researcher deleted successfully"}
raise HTTPException(status_code=404, detail="Researcher not found")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5000)

View File

@@ -0,0 +1,4 @@
fastapi==0.104.1
uvicorn==0.24.0
pydantic==2.5.0
requests==2.31.0