Форма развёртывания — Примеры
Пример 1 — Вызов зависимости без тайм-аута
Профиль пользователя забирается из другого сервиса. Когда тот сервис замедляется, перестаёт отвечать всё приложение. Скажите, какой отказ граница не изолировала, затем сделайте отказ локальным.
async function getProfile(id: string) {
const res = await fetchJson(userService + "/profile/" + id); // ждёт бесконечно
return { id, tier: res.tier };
}
// userService замедляется -> каждый обработчик запроса держит соединение
Решение
async function getProfile(id: string) {
const res = await fetchJson(userService + "/profile/" + id, {
timeoutMs: 200, retries: 1,
}).catch(() => null); // ухудшенный режим, а не зависание
return { id, tier: res?.tier ?? "standard" };
}
Разделение системы на части само по себе не изолирует отказ; оно создаёт новые режимы отказа, а вызывающий, который ждёт вечно, распространяет отказ через ту самую границу, которая должна была его защитить. Явный тайм-аут вместе с определённым ухудшенным ответом удерживают радиус поражения локальным: вызов профиля возвращает уровень по умолчанию, а остальное приложение продолжает обслуживать запросы. Этим и отличаются раздельные сервисы от независимых — разницу проектируют осознанно у каждого исходящего вызова.
Пример 2 — Распределённая система без названной причины
Небольшая команда делит приложение на шесть сервисов, потому что так выглядела эталонная архитектура, и теперь каждой функции нужны три согласованных развёртывания. Проверьте, было ли разделение оправданным, затем назовите подходящую форму.
сервисы: gateway, users, orders, billing, inventory, notifications
симптомы: изменение одного поля требует выпустить три сервиса по порядку;
один падающий сервис утягивает за собой два других
названные причины разделения: «микросервисы — это современно»
Решение
возможные ограничения, оправдывающие разделение:
- две команды должны поставлять в разном темпе измеримо -> если так, разделите этот шов
- конвейеру данных нужно собственное масштабирование измеримо -> если так, извлеките его
- «микросервисы — это современно» не ограничение -> границы нет
по умолчанию: одна развёртываемая единица, ясные внутренние границы, извлечение по потребности
Монолит с чистыми внутренними границами проще для понимания, тестирования и отладки, чем тот же код, разделённый по процессам, и при этом он оставляет вариант открытым: модуль можно извлечь позже, по одному, когда появится настоящая операционная потребность. Начать распределённо очень трудно откатить, и эта система заплатила операционные издержки шести развёртываемых единиц, сохранив связанность одной. Проверка проста: запишите, что нужно системе и чего не может дать одна единица — независимый темп релизов, другой профиль масштабирования, отказ, который нужно сдержать, — и разделяйте только там, где это предложение удаётся закончить.