← Назад к вопросам

Что отказоустойчивее - микросервис или монолит?

2.0 Middle🔥 221 комментариев
#DevOps и инфраструктура#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что отказоустойчивее — микросервис или монолит?

Отказоустойчивость (fault tolerance) — это способность системы продолжать работать при сбое отдельных компонентов. Здесь нет простого ответа — каждый подход имеет свои преимущества и недостатки.

Отказоустойчивость монолита

Недостатки:

  1. Отказ целого приложения Если упадёт один процесс монолита, недоступны все функции.
# Монолит: одно падение — всё падает
# api.py
from fastapi import FastAPI
app = FastAPI()

@app.get("/users")
async def get_users():
    return ["user1", "user2"]

@app.get("/products")
async def get_products():
    return ["product1", "product2"]

# Если возникает критическая ошибка в get_users,
# пользователи не смогут получить products
if some_error:
    raise Exception("Database connection failed")
    # Весь сервис перестаёт работать
  1. Каскадные отказы Ошибка в одном модуле может сломать другой.
# user_service.py
class UserService:
    def get_user(self, user_id):
        user = db.query(User).get(user_id)  # DB error
        self.log_analytics(user)  # Ошибка в другом сервисе
        return user

# Если log_analytics упадёт, весь get_user не работает
  1. Сложность восстановления Приходится перезагружать всё приложение, даже если ошибка локальна.

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

  1. Нет сетевых разделов Компоненты работают в одном процессе, нет проблем с сетевыми отказами между компонентами.
# Монолит: всё работает локально
class OrderService:
    def create_order(self, user_id, items):
        user = UserService().get_user(user_id)  # Быстро
        inventory = InventoryService().check_stock(items)  # Быстро
        # Нет проблем с таймаутами или потерей сообщений
        order = Order(user=user, items=items)
        db.add(order)
        db.commit()
  1. Транзакции Можно использовать ACID транзакции для консистентности.
from django.db import transaction

with transaction.atomic():
    user = User.objects.create(username="john")
    order = Order.objects.create(user=user)
    # Если что-то упадёт, всё откатывается

Отказоустойчивость микросервисов

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

  1. Изоляция сбоев Если упадёт один сервис, другие продолжают работать.
# users-service/main.py
@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"id": user_id, "name": "John"}

# products-service/main.py
@app.get("/products/{product_id}")
async def get_product(product_id: int):
    return {"id": product_id, "name": "Laptop"}

# api-gateway/main.py
@app.get("/api/dashboard")
async def dashboard():
    try:
        users = httpx.get("http://users-service:8000/users").json()
    except:
        users = []  # Graceful degradation
    
    try:
        products = httpx.get("http://products-service:8000/products").json()
    except:
        products = []  # API продолжает работать
    
    return {"users": users, "products": products}
  1. Масштабирование отдельных частей Можно добавить больше инстансов нужного сервиса.
# Проблема с users-service? Добавляем ещё инстансы
kubectl scale deployment users-service --replicas=5

# orders-service работает нормально, не нужно его масштабировать
  1. Независимое развёртывание Можно обновить один сервис без перезагрузки других.

Недостатки:

  1. Сетевые разделы (Network Partitions) Если связь между сервисами разорвана, возникают проблемы с консистентностью.
# order-service пытается обновить inventory
try:
    response = httpx.get(
        "http://inventory-service:8000/check-stock",
        timeout=5
    )
except httpx.TimeoutException:
    # Что делать? Считать stock доступным или нет?
    # Это может привести к overselling
    pass
  1. Распределённые транзакции Не могу использовать простые ACID транзакции.
# order-service
async def create_order(user_id, items):
    order = await db.create_order(user_id, items)
    
    # Попытка зарезервировать stock
    try:
        await inventory_service.reserve_stock(items)
    except:
        # Откатить заказ?
        await db.cancel_order(order.id)
        raise
    
    # Что если после reserve_stock сбой?
    # Нужна компенсирующая транзакция (Saga pattern)
  1. Сложность отладки Ошибки разбросаны по разным сервисам, нужны распределённые логи и трейсы.

Паттерны отказоустойчивости

1. Circuit Breaker Предотвращает каскадные отказы.

from pybreaker import CircuitBreaker

db_breaker = CircuitBreaker(fail_max=5, reset_timeout=60)

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    @db_breaker
    def fetch_user():
        return db.query(User).get(user_id)
    
    try:
        return fetch_user()
    except:
        # Вернуть кэшированные данные или default
        return {"id": user_id, "name": "Unknown"}

2. Retry Logic Повторить операцию при временном отказе.

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10)
)
async def call_external_service():
    response = await httpx.get("http://external-service/api")
    return response.json()

3. Fallback Использовать альтернативный источник данных.

async def get_user_data(user_id: int):
    # Попытка 1: основная БД
    try:
        return await main_db.get_user(user_id)
    except:
        pass
    
    # Попытка 2: кэш
    try:
        return await redis_cache.get(f"user:{user_id}")
    except:
        pass
    
    # Попытка 3: читаемая реплика
    try:
        return await read_replica.get_user(user_id)
    except:
        pass
    
    # Fallback: минимальные данные
    return {"id": user_id, "name": "Unknown"}

4. Saga Pattern Для распределённых транзакций.

# Шаг 1: создать заказ
order = await order_service.create(user_id, items)

try:
    # Шаг 2: зарезервировать stock
    await inventory_service.reserve(items)
except:
    # Шаг 2a: откатить заказ (компенсирующая транзакция)
    await order_service.cancel(order.id)
    raise

try:
    # Шаг 3: обработать платёж
    await payment_service.charge(user_id, order.total)
except:
    # Шаг 3a: вернуть stock
    await inventory_service.release(items)
    # Шаг 3b: откатить заказ
    await order_service.cancel(order.id)
    raise

Сравнение

АспектМонолитМикросервисы
Отказ целого сервисаВысокий рискНизкий риск
Отказ компонентаВлияет на всёТолько на сервис
Сетевые разделыНет проблемыБольшая проблема
Простота трансакцийВысокаяНизкая
Каскадные отказыВысокий рискМожно предотвратить
Сложность реализации отказоустойчивостиНизкаяВысокая

Заключение

Микросервисы теоретически более отказоустойчивы благодаря изоляции, но требуют сложных паттернов (Circuit Breaker, Saga, Retry) для реальной отказоустойчивости. Монолит проще реализовать, но менее гибкий. Выбор зависит от требований проекта и готовности к сложности.

Что отказоустойчивее - микросервис или монолит? | PrepBro