Microservices

Архитектура распределённых систем

Современный подход к разработке масштабируемых приложений

Что такое Microservices?

Microservices — архитектурный стиль, при котором приложение разбивается на множество независимых сервисов, каждый из которых отвечает за свою бизнес-функцию.

🔹 Monolith vs Microservices

┌─────────────────────────────┐
│      MONOLITH               │
│  ┌───────────────────────┐  │
│  │  User Service         │  │
│  │  Order Service        │  │
│  │  Payment Service      │  │
│  │  Notification Service │  │
│  │  Database             │  │
│  └───────────────────────┘  │
└─────────────────────────────┘
        Один процесс
        Одна база данных
        Одно развертывание

🔹 Microservices

┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ User Service │  │Order Service │  │Payment Service│
│   :8001      │  │   :8002      │  │   :8003       │
│   ┌──────┐   │  │   ┌──────┐   │  │   ┌──────┐   │
│   │ DB   │   │  │   │ DB   │   │  │   │ DB   │   │
│   └──────┘   │  │   └──────┘   │  │   └──────┘   │
└──────┬───────┘  └──────┬───────┘  └──────┬───────┘
       │                 │                 │
       └─────────────────┴─────────────────┘
                    API Gateway
                    :8080

Каждый сервис — отдельный процесс, своя БД

🔹 Характеристики Microservices

  • Независимое развертывание — каждый сервис деплоится отдельно
  • Независимая разработка — команды работают независимо
  • Технологическое разнообразие — можно использовать разные языки/фреймворки
  • Масштабируемость — масштабируем только нужные сервисы
  • Отказоустойчивость — падение одного сервиса не ломает всю систему

Пример: E-Commerce Platform

🔹 Разбиение на сервисы

┌─────────────────────────────────────────┐
│         API Gateway (Kong/Nginx)        │
└─────────┬───────────────────────────────┘
          │
    ┌─────┴─────┬──────────┬──────────┬──────────┐
    │           │          │          │          │
┌───┴────┐ ┌───┴────┐ ┌───┴────┐ ┌───┴────┐ ┌───┴────┐
│  User  │ │ Order  │ │Payment │ │Catalog │ │Shipping│
│Service │ │Service │ │Service │ │Service │ │Service │
│:8001   │ │:8002   │ │:8003   │ │:8004   │ │:8005   │
└───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘
    │          │          │          │          │
┌───┴────┐ ┌───┴────┐ ┌───┴────┐ ┌───┴────┐ ┌───┴────┐
│Postgres│ │Postgres│ │  Redis │ │MongoDB │ │Postgres│
└────────┘ └────────┘ └────────┘ └────────┘ └────────┘

🔹 User Service

# user-service/main.py
from fastapi import FastAPI
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

app = FastAPI()
engine = create_engine("postgresql://user:pass@localhost/user_db")
Session = sessionmaker(bind=engine)

@app.get("/users/{user_id}")
def get_user(user_id: int):
    session = Session()
    user = session.query(User).filter(User.id == user_id).first()
    return {
        "id": user.id,
        "name": user.name,
        "email": user.email
    }

@app.post("/users")
def create_user(user_data: UserCreate):
    session = Session()
    user = User(name=user_data.name, email=user_data.email)
    session.add(user)
    session.commit()
    return {"id": user.id, "name": user.name}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8001)

🔹 Order Service

# order-service/main.py
from fastapi import FastAPI
import httpx

app = FastAPI()

# Клиент для User Service
user_service_url = "http://user-service:8001"

@app.post("/orders")
async def create_order(order_data: OrderCreate):
    # 1. Проверяем пользователя через User Service
    async with httpx.AsyncClient() as client:
        user_response = await client.get(
            f"{user_service_url}/users/{order_data.user_id}"
        )
        if user_response.status_code != 200:
            raise HTTPException(404, "User not found")
        
        user = user_response.json()
    
    # 2. Создаём заказ
    order = Order(
        user_id=order_data.user_id,
        items=order_data.items,
        total=calculate_total(order_data.items)
    )
    db.session.add(order)
    db.session.commit()
    
    # 3. Отправляем событие (async)
    event_bus.publish("order_created", {
        "order_id": order.id,
        "user_id": order.user_id,
        "total": order.total
    })
    
    return {"order_id": order.id, "status": "created"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8002)

🔹 API Gateway

# api-gateway/main.py
from fastapi import FastAPI, HTTPException
import httpx

app = FastAPI()

SERVICES = {
    "users": "http://user-service:8001",
    "orders": "http://order-service:8002",
    "payments": "http://payment-service:8003"
}

@app.api_route("/{service}/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def proxy(service: str, path: str, request: Request):
    if service not in SERVICES:
        raise HTTPException(404, "Service not found")
    
    service_url = SERVICES[service]
    url = f"{service_url}/{path}"
    
    # Проксируем запрос
    async with httpx.AsyncClient() as client:
        response = await client.request(
            method=request.method,
            url=url,
            headers=dict(request.headers),
            content=await request.body()
        )
        return Response(
            content=response.content,
            status_code=response.status_code,
            headers=dict(response.headers)
        )

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8080)

💡 API Gateway — единая точка входа для всех клиентов

Коммуникация между сервисами

🔹 Synchronous (HTTP/REST)

# Order Service вызывает User Service
import httpx

async def validate_user(user_id: int):
    async with httpx.AsyncClient() as client:
        response = await client.get(f"http://user-service/users/{user_id}")
        return response.json()

# Проблемы:
# - Зависимость от доступности сервиса
# - Медленно (сетевая задержка)
# - Каскадные сбои

💡 Используйте для операций, которые требуют немедленного ответа

🔹 Asynchronous (Message Queue)

# Order Service публикует событие
from redis import Redis
import json

redis = Redis(host='redis', port=6379)

def create_order(order_data):
    order = Order.create(order_data)
    
    # Публикуем событие (не ждём ответа)
    redis.publish("order_created", json.dumps({
        "order_id": order.id,
        "user_id": order.user_id,
        "total": order.total
    }))
    
    return order

# Payment Service подписывается на событие
def on_order_created(message):
    data = json.loads(message['data'])
    # Обрабатываем платеж асинхронно
    process_payment(data['order_id'], data['total'])

redis_subscriber = redis.pubsub()
redis_subscriber.subscribe(**{"order_created": on_order_created})

💡 Используйте для операций, которые можно выполнить асинхронно

🔹 Service Mesh (Istio, Linkerd)

┌─────────────┐
│   Service   │
│   (App)     │
└──────┬──────┘
       │
┌──────┴──────┐
│  Sidecar    │ ← Service Mesh Proxy
│  (Envoy)    │   - Load balancing
└──────┬──────┘   - Circuit breaker
       │          - Retry
       │          - Monitoring
       ↓
    Network

💡 Service Mesh управляет коммуникацией между сервисами

Паттерны Microservices

🔹 API Gateway Pattern

Единая точка входа для всех клиентов.

Client → API Gateway → [Service1, Service2, Service3]

Функции:
- Маршрутизация
- Аутентификация
- Rate limiting
- Логирование
- Мониторинг

💡 Примеры: Kong, AWS API Gateway, Nginx

🔹 Circuit Breaker Pattern

Защита от каскадных сбоев.

from circuitbreaker import circuit

@circuit(failure_threshold=5, recovery_timeout=60)
async def call_user_service(user_id: int):
    async with httpx.AsyncClient() as client:
        response = await client.get(f"http://user-service/users/{user_id}")
        return response.json()

# Если сервис падает 5 раз подряд,
# Circuit Breaker открывается на 60 секунд
# Все запросы сразу возвращают ошибку
# (не нагружают упавший сервис)

🔹 Saga Pattern

Управление распределёнными транзакциями.

# Создание заказа требует действий в нескольких сервисах
class CreateOrderSaga:
    def __init__(self):
        self.steps = [
            self.reserve_inventory,
            self.process_payment,
            self.create_shipment
        ]
        self.compensations = [
            self.release_inventory,
            self.refund_payment,
            self.cancel_shipment
        ]
    
    async def execute(self, order_data):
        completed_steps = []
        try:
            for step in self.steps:
                await step(order_data)
                completed_steps.append(step)
        except Exception as e:
            # Откатываем выполненные шаги
            for step in reversed(completed_steps):
                compensation = self.get_compensation(step)
                await compensation(order_data)
            raise e

💡 Если один шаг падает — откатываем все предыдущие

🔹 Database per Service

Каждый сервис имеет свою базу данных.

User Service → User Database (PostgreSQL)
Order Service → Order Database (PostgreSQL)
Catalog Service → Catalog Database (MongoDB)
Analytics Service → Analytics Database (ClickHouse)

Правила:
- Сервис не может напрямую обращаться к БД другого сервиса
- Коммуникация только через API
- Каждый сервис выбирает БД под свои нужды

Преимущества Microservices

  • Независимое развертывание — деплоим только изменённые сервисы
  • Технологическое разнообразие — Python для ML, Go для API, Java для backend
  • Масштабируемость — масштабируем только нагруженные сервисы
  • Устойчивость к сбоям — падение одного сервиса не ломает всю систему
  • Командная независимость — команды работают над своими сервисами
  • Гибкость — легко добавлять новые сервисы

Недостатки Microservices

  • Сложность — больше компонентов, больше точек отказа
  • Сетевая задержка — вызовы между сервисами медленнее локальных
  • Распределённые транзакции — сложно обеспечить консистентность
  • Операционная сложность — нужно управлять множеством сервисов
  • Дублирование кода — общая логика может дублироваться
  • Тестирование — сложнее тестировать систему целиком

💡 Когда использовать Microservices?

Сценарий Рекомендация
Большая команда (>10 разработчиков) ✅ Подходит
Разные требования к масштабированию ✅ Подходит
Разные технологии для разных частей ✅ Подходит
Маленькая команда (<5 разработчиков) ❌ Не подходит (начните с монолита)
Простое приложение ❌ Избыточно

Инструменты для Microservices

🔹 Контейнеризация

# Dockerfile
FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001"]

# docker-compose.yml
version: '3.8'
services:
  user-service:
    build: ./user-service
    ports:
      - "8001:8001"
    environment:
      - DATABASE_URL=postgresql://user:pass@postgres:5432/user_db
  
  order-service:
    build: ./order-service
    ports:
      - "8002:8002"
    depends_on:
      - user-service

🔹 Оркестрация (Kubernetes)

# kubernetes/user-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: user-service:latest
        ports:
        - containerPort: 8001
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user-service
  ports:
  - port: 8001
    targetPort: 8001

🔹 Мониторинг

  • Prometheus — сбор метрик
  • Grafana — визуализация метрик
  • Jaeger — распределённая трассировка
  • ELK Stack — логирование (Elasticsearch, Logstash, Kibana)
  • Zipkin — трассировка запросов

🔹 Message Brokers

  • RabbitMQ — классический message broker
  • Apache Kafka — распределённый streaming platform
  • Redis Pub/Sub — простой pub/sub для небольших систем
  • Amazon SQS — управляемый message queue в AWS

Антипаттерны Microservices

❌ Distributed Monolith

Микросервисы, которые слишком тесно связаны.

# ❌ Сервисы вызывают друг друга синхронно
order_service → user_service (синхронно)
order_service → payment_service (синхронно)
order_service → inventory_service (синхронно)

# Если один падает — падает весь поток
# Это не микросервисы, это распределённый монолит!

✅ Решение: используйте асинхронную коммуникацию, события

❌ Shared Database

Несколько сервисов используют одну БД.

# ❌ Все сервисы обращаются к одной БД
user_service → shared_database
order_service → shared_database
payment_service → shared_database

# Проблемы:
# - Сложно изменить схему
# - Невозможно независимое развертывание
# - Сложно масштабировать

✅ Решение: Database per Service

❌ Too Many Services

Слишком мелкое разбиение.

# ❌ Слишком много сервисов
user-service
user-profile-service
user-settings-service
user-preferences-service
user-notifications-service

# Проблемы:
# - Сложность управления
# - Много сетевых вызовов
# - Сложное тестирование

✅ Решение: группируйте связанные функции в один сервис

🎯 Ключевые принципы Microservices

  1. Независимость — сервисы независимы в разработке и развертывании
  2. Database per Service — каждый сервис имеет свою БД
  3. API First — сервисы общаются через API
  4. Failure Isolation — падение одного сервиса не ломает систему
  5. Observability — мониторинг, логирование, трассировка
  6. Automation — автоматизация развертывания и тестирования
"Начните с монолита. Разбивайте на микросервисы только когда почувствуете боль от монолита."
— Martin Fowler

📚 Дополнительные ресурсы

  • Книга: "Building Microservices" by Sam Newman
  • Статьи: martinfowler.com/microservices
  • Курсы: microservices.io
  • Инструменты: Kubernetes, Docker, Istio, Prometheus