Merge pull request 'artamonova_tatyana_lab_3 is ready' (#138) from artamonova_tatyana_lab_3 into main
Reviewed-on: #138
This commit is contained in:
commit
22cb8b936f
15
artamonova_tatyana_lab_3/docker-compose.yml
Normal file
15
artamonova_tatyana_lab_3/docker-compose.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
vacancies-service:
|
||||||
|
build: ./vacancy-service
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
resumes-service:
|
||||||
|
build: ./resume-service
|
||||||
|
ports:
|
||||||
|
- "5001:5001"
|
||||||
|
gateway:
|
||||||
|
image: nginx:latest
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/conf.d/default.conf
|
19
artamonova_tatyana_lab_3/nginx.conf
Normal file
19
artamonova_tatyana_lab_3/nginx.conf
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
upstream vacancies {
|
||||||
|
server vacancies-service:5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream resumes {
|
||||||
|
server resumes-service:5001;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
location /vacancies {
|
||||||
|
proxy_pass http://vacancies;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /resumes {
|
||||||
|
proxy_pass http://resumes;
|
||||||
|
}
|
||||||
|
}
|
35
artamonova_tatyana_lab_3/readme.md
Normal file
35
artamonova_tatyana_lab_3/readme.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
## Лабораторная работа №3 ПИбд-42 Артамоновой Татьяны
|
||||||
|
|
||||||
|
### Цель:
|
||||||
|
* Реализовать два микросервиса, которые взаимодействуют друг с другом через синхронный обмен сообщениями (HTTP-запросы). Для доступа к микросервисам используется шлюз Nginx, реализованный с помощью Docker Compose.
|
||||||
|
|
||||||
|
### Технологии:
|
||||||
|
|
||||||
|
* Python: Язык программирования для реализации микросервисов.
|
||||||
|
* Flask: Фреймворк Python для создания веб-приложений, использован для создания REST API микросервисов.
|
||||||
|
* requests: Библиотека Python для отправки HTTP-запросов, использован для синхронного обмена сообщениями между микросервисами.
|
||||||
|
* flask_cors: Расширение Flask, которое позволяет микросервисам получать доступ к данным из других доменов.
|
||||||
|
* Docker: Технология контейнеризации для упаковки и запуска микросервисов.
|
||||||
|
* Docker Compose: Инструмент для определения и управления многоконтейнерными приложениями, использован для запуска микросервисов и шлюза Nginx.
|
||||||
|
* Nginx: Сетевой прокси-сервер, использован как шлюз для доступа к микросервисам.
|
||||||
|
|
||||||
|
### Функциональность:
|
||||||
|
|
||||||
|
#### Микросервис vacancies-service:
|
||||||
|
* Реализует CRUD операции для вакансий (GET, POST, PUT, DELETE).
|
||||||
|
* Сохраняет данные о вакансиях в памяти (в словаре vacancies).
|
||||||
|
* Получает информацию о резюме из микросервиса resumes-service через HTTP-запрос.
|
||||||
|
* Включает информацию о резюме в ответ JSON для вакансии.
|
||||||
|
#### Микросервис resumes-service:
|
||||||
|
* Реализует CRUD операции для резюме (GET, POST, PUT, DELETE).
|
||||||
|
* Сохраняет данные о резюме в памяти (в словаре resumes).
|
||||||
|
#### Шлюз Nginx:
|
||||||
|
* Перенаправляет HTTP-запросы на соответствующие микросервисы.
|
||||||
|
* Предоставляет единую точку входа для доступа к микросервисам.
|
||||||
|
|
||||||
|
### Запуск программы:
|
||||||
|
|
||||||
|
* Запуск команды docker-compose up -d
|
||||||
|
|
||||||
|
### Ссылка на видео:
|
||||||
|
https://vk.com/artamonovat?z=video212084908_456239358%2Fvideos212084908%2Fpl_212084908_-2
|
11
artamonova_tatyana_lab_3/resume-service/Dockerfile
Normal file
11
artamonova_tatyana_lab_3/resume-service/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
FROM python:3.9
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["python", "resume.py"]
|
3
artamonova_tatyana_lab_3/resume-service/requirements.txt
Normal file
3
artamonova_tatyana_lab_3/resume-service/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
flask
|
||||||
|
requests
|
||||||
|
flask_cors
|
79
artamonova_tatyana_lab_3/resume-service/resume.py
Normal file
79
artamonova_tatyana_lab_3/resume-service/resume.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import uuid
|
||||||
|
import json
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from flask_cors import CORS
|
||||||
|
|
||||||
|
|
||||||
|
class Resume:
|
||||||
|
def __init__(self, uuid, full_name, skills, experience, phone, email):
|
||||||
|
self.uuid = uuid
|
||||||
|
self.full_name = full_name
|
||||||
|
self.skills = skills
|
||||||
|
self.experience = experience
|
||||||
|
self.phone = phone
|
||||||
|
self.email = email
|
||||||
|
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
CORS(app)
|
||||||
|
|
||||||
|
resumes = {}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/resumes", methods=["GET"])
|
||||||
|
def get_resumes():
|
||||||
|
return jsonify([resume.__dict__ for resume in resumes.values()])
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/resumes/<resume_uuid>", methods=["GET"])
|
||||||
|
def get_resume(resume_uuid):
|
||||||
|
resume = resumes.get(resume_uuid)
|
||||||
|
if resume:
|
||||||
|
return jsonify(resume.__dict__)
|
||||||
|
else:
|
||||||
|
return jsonify({"error": "Resume not found"}), 404
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/resumes", methods=["POST"])
|
||||||
|
def create_resume():
|
||||||
|
data = request.get_json()
|
||||||
|
resume_uuid = str(uuid.uuid4())
|
||||||
|
resume = Resume(
|
||||||
|
resume_uuid,
|
||||||
|
data["full_name"],
|
||||||
|
data["skills"],
|
||||||
|
data["experience"],
|
||||||
|
data["phone"],
|
||||||
|
data["email"],
|
||||||
|
)
|
||||||
|
resumes[resume_uuid] = resume
|
||||||
|
return jsonify(resume.__dict__), 201
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/resumes/<resume_uuid>", methods=["PUT"])
|
||||||
|
def update_resume(resume_uuid):
|
||||||
|
resume = resumes.get(resume_uuid)
|
||||||
|
if resume:
|
||||||
|
data = request.get_json()
|
||||||
|
resume.full_name = data.get("full_name", resume.full_name)
|
||||||
|
resume.skills = data.get("skills", resume.skills)
|
||||||
|
resume.experience = data.get("experience", resume.experience)
|
||||||
|
resume.phone = data.get("phone", resume.phone)
|
||||||
|
resume.email = data.get("email", resume.email)
|
||||||
|
return jsonify(resume.__dict__)
|
||||||
|
else:
|
||||||
|
return jsonify({"error": "Resume not found"}), 404
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/resumes/<resume_uuid>", methods=["DELETE"])
|
||||||
|
def delete_resume(resume_uuid):
|
||||||
|
resume = resumes.get(resume_uuid)
|
||||||
|
if resume:
|
||||||
|
del resumes[resume_uuid]
|
||||||
|
return "", 200
|
||||||
|
else:
|
||||||
|
return jsonify({"error": "Resume not found"}), 404
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(debug=True, host="0.0.0.0", port=5001)
|
11
artamonova_tatyana_lab_3/vacancy-service/Dockerfile
Normal file
11
artamonova_tatyana_lab_3/vacancy-service/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
FROM python:3.9
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["python", "vacancy.py"]
|
@ -0,0 +1,3 @@
|
|||||||
|
flask
|
||||||
|
requests
|
||||||
|
flask_cors
|
124
artamonova_tatyana_lab_3/vacancy-service/vacancy.py
Normal file
124
artamonova_tatyana_lab_3/vacancy-service/vacancy.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import uuid
|
||||||
|
import json
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from flask_cors import CORS
|
||||||
|
import requests
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
from requests.packages.urllib3.util.retry import Retry
|
||||||
|
|
||||||
|
class Vacancy:
|
||||||
|
def __init__(self, uuid, title, company, description, salary, resume_uuid):
|
||||||
|
self.uuid = uuid
|
||||||
|
self.title = title
|
||||||
|
self.company = company
|
||||||
|
self.description = description
|
||||||
|
self.salary = salary
|
||||||
|
self.resume_uuid = resume_uuid
|
||||||
|
self.resume_info = None
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
if self.resume_info:
|
||||||
|
return {
|
||||||
|
"uuid": self.uuid,
|
||||||
|
"title": self.title,
|
||||||
|
"company": self.company,
|
||||||
|
"description": self.description,
|
||||||
|
"salary": self.salary,
|
||||||
|
"resume_uuid": self.resume_uuid,
|
||||||
|
"resume_info": self.resume_info
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"uuid": self.uuid,
|
||||||
|
"title": self.title,
|
||||||
|
"company": self.company,
|
||||||
|
"description": self.description,
|
||||||
|
"salary": self.salary,
|
||||||
|
"resume_uuid": self.resume_uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
CORS(app)
|
||||||
|
|
||||||
|
vacancies = {}
|
||||||
|
|
||||||
|
@app.route("/vacancies", methods=["GET"])
|
||||||
|
def get_vacancies():
|
||||||
|
return jsonify([vacancy.to_dict() for vacancy in vacancies.values()])
|
||||||
|
|
||||||
|
@app.route("/vacancies/<vacancy_uuid>", methods=["GET"])
|
||||||
|
def get_vacancy(vacancy_uuid):
|
||||||
|
vacancy = vacancies.get(vacancy_uuid)
|
||||||
|
if vacancy:
|
||||||
|
if not vacancy.resume_info:
|
||||||
|
vacancy.resume_info = get_resume_info(vacancy.resume_uuid)
|
||||||
|
return jsonify(vacancy.to_dict())
|
||||||
|
else:
|
||||||
|
return jsonify({"error": "Vacancy not found"}), 404
|
||||||
|
|
||||||
|
@app.route("/vacancies", methods=["POST"])
|
||||||
|
def create_vacancy():
|
||||||
|
data = request.get_json()
|
||||||
|
vacancy_uuid = str(uuid.uuid4())
|
||||||
|
vacancy = Vacancy(
|
||||||
|
vacancy_uuid,
|
||||||
|
data["title"],
|
||||||
|
data["company"],
|
||||||
|
data["description"],
|
||||||
|
data["salary"],
|
||||||
|
data["resume_uuid"],
|
||||||
|
)
|
||||||
|
vacancies[vacancy_uuid] = vacancy
|
||||||
|
vacancy.resume_info = get_resume_info(vacancy.resume_uuid)
|
||||||
|
return jsonify(vacancy.to_dict()), 201
|
||||||
|
|
||||||
|
@app.route("/vacancies/<vacancy_uuid>", methods=["PUT"])
|
||||||
|
def update_vacancy(vacancy_uuid):
|
||||||
|
vacancy = vacancies.get(vacancy_uuid)
|
||||||
|
if vacancy:
|
||||||
|
data = request.get_json()
|
||||||
|
vacancy.title = data.get("title", vacancy.title)
|
||||||
|
vacancy.company = data.get("company", vacancy.company)
|
||||||
|
vacancy.description = data.get("description", vacancy.description)
|
||||||
|
vacancy.salary = data.get("salary", vacancy.salary)
|
||||||
|
vacancy.resume_uuid = data.get("resume_uuid", vacancy.resume_uuid)
|
||||||
|
vacancy.resume_info = get_resume_info(vacancy.resume_uuid)
|
||||||
|
return jsonify(vacancy.to_dict())
|
||||||
|
else:
|
||||||
|
return jsonify({"error": "Vacancy not found"}), 404
|
||||||
|
|
||||||
|
@app.route("/vacancies/<vacancy_uuid>", methods=["DELETE"])
|
||||||
|
def delete_vacancy(vacancy_uuid):
|
||||||
|
vacancy = vacancies.get(vacancy_uuid)
|
||||||
|
if vacancy:
|
||||||
|
del vacancies[vacancy_uuid]
|
||||||
|
return "", 200
|
||||||
|
else:
|
||||||
|
return jsonify({"error": "Vacancy not found"}), 404
|
||||||
|
|
||||||
|
def get_resume_info(resume_uuid):
|
||||||
|
url = f'http://resumes-service:5001/resumes/{resume_uuid}'
|
||||||
|
|
||||||
|
# Настройка retry механизма
|
||||||
|
retries = Retry(
|
||||||
|
total=3, # Максимальное количество повторов
|
||||||
|
status_forcelist=[429, 500, 502, 503, 504], # Коды статуса, для которых нужно повторить запрос
|
||||||
|
backoff_factor=0.3, # Время ожидания перед повторной попыткой
|
||||||
|
)
|
||||||
|
adapter = HTTPAdapter(max_retries=retries)
|
||||||
|
http = requests.Session()
|
||||||
|
http.mount("https://", adapter)
|
||||||
|
http.mount("http://", adapter)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = http.get(url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.json()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Ошибка при запросе к resumes-service: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(debug=True, host="0.0.0.0", port=5000)
|
Loading…
Reference in New Issue
Block a user