artamonova_tatyana_lab_3 is ready #138
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