Перейти к содержимому

Устойчивость — не магия, а набор явных правил: circuit breaker, повторы с экспоненциальной выдержкой и разбросом, каскад таймаутов, изоляция по отсекам, плавная деградация, идемпотентность. Каждое правило описывается в одну фразу, но я регулярно вижу команды, которые знают слова и не применяют их дисциплинированно: повторы без разброса (и все клиенты бьются в дверь синхронно при первом же сбое), circuit breaker без стратегии восстановления («открылся навсегда»), идемпотентность в режиме «допилим потом», хотя выкатка завтра. Лист — про дисциплину применения. Соседний к Capacity Planning и SLO Engineering под L1 Reliability Engineering. Граница: там готовятся к нагрузке, здесь выживают, когда подготовка не сработала.

Главный навык на уровне L4 — правильно реализовать экспоненциальную выдержку с разбросом. Я регулярно вижу команды, которые написали повторы «с выдержкой», но забыли про разброс, — и при первом же сбое получили шторм повторов, добивший сервис, который только начал вставать. Лучший разбор написал Marc Brooker в блоге AWS в 2015 году: три варианта разброса с симуляциями и графиками. Час чтения и год пользы.

L3

  • Знает базовый набор паттернов (circuit breaker, повторы, таймауты, запасной ответ); применяет их через библиотеки своего стека (Polly, resilience4j, Tenacity, retry-axios), а не пишет заново.
  • Понимает разницу между пробами liveness и readiness; пишет адекватные проверки здоровья: поверхностную «процесс жив» и глубокую «зависимости доступны».

L4

  • Реализует повторы с экспоненциальной выдержкой и разбросом; понимает, почему без разброса получается синхронная волна и почему бесконечные повторы без circuit breaker раскручивают каскад.
  • Выстраивает таймауты иерархически: внутренний меньше внешнего с запасом на повторы, никаких сетевых умолчаний по умолчанию, проброс оставшегося времени между сервисами.

L5

  • Проектирует изоляцию по отсекам: пулы соединений, пулы потоков, разделённые очереди — чтобы перегрузка одной зависимости не съедала ресурсы остальных.
  • Реализует плавную деградацию с явными уровнями критичности: feature flags для отключения второстепенного, запасные ответы, отдача из кеша. Режим деградации описан словами и проверяется на game day.
  • Делает идемпотентность обязательной для всего, что можно повторять: ключи идемпотентности, ETag, условные записи, исходящий журнал транзакций.

L6+

  • Проектирует сброс нагрузки и обратное давление: приоритеты по критичности, отказ второстепенному трафику при перегрузке, контроль допуска по глубине очереди, бюджет повторов на уровне сервиса.
  • Внедряет chaos engineering как способ проверить, что все эти механизмы действительно работают.
  • Michael Nygard — Release It! Design and Deploy Production-Ready Software (Pragmatic Bookshelf, 2-е изд., 2018). Каноническая книга по теме: главы про паттерны и антипаттерны стабильности — circuit breaker, отсеки, устойчивое состояние, быстрый отказ. Спустя двадцать лет всё ещё лучший единый источник.
  • Cindy Sridharan — Distributed Systems Observability (O’Reilly, 2018). Связь устойчивости и наблюдаемости: как увидеть, что circuit breaker открыт, и как поймать шторм повторов в метриках.
  • Addressing Cascading Failures — SRE Book, глава 22. Канонический разбор: откуда берётся каскад, как повторы усиливают нагрузку, что такое перегруженный сервер и запрос-убийца.
  • Handling Overload — SRE Book, глава 21. Дополняет предыдущую: придушивание на стороне клиента, уровни критичности, бюджеты повторов, дедлайны и их проброс.
  • Marc Brooker — Exponential Backoff and Jitter (AWS Architecture Blog, 2015). Главный публичный кейс — см. ниже.
  • Netflix Tech Blog — Making the Netflix API more resilient. История появления Hystrix и обоснование изоляции по отсекам на живой системе.
  • Мой разбор — Digital Immune System: инженерия устойчивости как продукт (jtprog.ru, август 2026). Раздел про право на отказ и организованную деградацию — про то, чего в главах SRE Book выше нет: деградация срабатывает только тогда, когда заранее решено, какие функции гаснут первыми и кто это решение принимает. Входная точка в саму концепцию — мой более ранний обзор Digital Immune System (февраль 2026).
  • resilience4j (Java) — современная замена Hystrix. Circuit breaker, повторы, ограничитель частоты, отсеки и таймауты собираются как отдельные сочетаемые модули, а не монолитом. По моим наблюдениям, для нового кода на Java чаще выбирают именно её.
  • Polly (.NET) — каноническая библиотека для той же задачи. Текучий интерфейс, те же паттерны.
  • Tenacity (Python) — простая библиотека повторов с богатой настройкой выдержки, разброса и условий остановки. Если кроме повторов ничего не нужно, её достаточно.
  • Envoy / Istio circuit breaking — уровень service mesh: размыкание, отсев неисправных адресатов, повторы прямо в прокси, без правок кода. Подходит, когда сервис менять нельзя или в языке нет приличной библиотеки.
  • Chaos Mesh / Litmus / AWS Fault Injection Service — чем проверять, что все эти механизмы срабатывают.

Главный публичный кейс на тему выдержки — Marc Brooker, «Exponential Backoff and Jitter» (AWS Architecture Blog, 2015). Симуляции в статье показывают: при общем сбое клиенты с линейными повторами бьются синхронно одной волной, и адресат не успевает восстановиться. Равномерный разброс — случайная величина между половиной и полной выдержкой — размазывает нагрузку по времени. Для клиентов в стиле AWS ещё лучше работает декоррелированный вариант; формула и графики там же. Если кто-то в команде написал повторы без разброса, отправляйте на эту статью первым делом. Час чтения окупается на первом же сбое.

Три правила, вокруг которых крутится всё остальное. Повторы живут только на идемпотентных операциях, и идемпотентность — предусловие, а не «допилим потом»: каждый повтор без ключа рано или поздно превращается в двойную запись или двойное списание. Либо идемпотентность зашита в контракт API, либо повторы выключены, третьего нет. Экспоненциальная выдержка с разбросом обязательна, потому что «пять раз с интервалом в секунду» при сбое соседа означает, что все клиенты бьются синхронно. И проверка здоровья — не проверка бизнес-логики: liveness, который лезет в базу, при её недоступности заставит Kubernetes перезапустить все поды разом и превратит частный отказ в каскад. liveness отвечает на вопрос «процесс жив», readiness — «готов принимать трафик»; отказ базы гасит второе и не трогает первое.

Circuit breaker без явной стратегии восстановления открывается навсегда. Схему «разомкнут — пробуем — замкнут» пишут все, а вот условие возврата продумывают редко. Работает так: после паузы пропускается один пробный запрос, успех замыкает цепь, неудача возвращает в разомкнутое состояние с увеличенным интервалом. Метрики состояния — обязательная часть дашборда сервиса, иначе в инциденте никто не понимает, сработал он или нет.

Таймауты выстраиваются каскадом: внутренний меньше внешнего с запасом на повторы. Классическая ошибка — тридцать секунд у внешнего клиента и те же тридцать у внутреннего вызова: внутренний всегда «успевает» по своему таймеру, но отвечать уже некому, клиент отвалился. Каждый уровень отсекается раньше родителя на величину бюджета повторов плюс запас. Проброс оставшегося времени дальше по цепочке — через заголовки gRPC или метаданные — уровень повыше, но он закрывает целый класс каскадных проблем.

Плавная деградация требует явных уровней критичности. «При перегрузке что-нибудь отключим» — не план: пока запросы и сервисы не разложены на критичные, важные и те, которыми можно пожертвовать, решать в моменте нечем. В SRE Book предлагают свою разметку — critical, shedable+, shedable. Признак зрелого сервиса простой: режим деградации описан словами и регулярно проверяется на game day. По моим наблюдениям, без разметки критичности деградация существует только на бумаге и в перегрузке не работает.

Паттерны без наблюдаемости — слепое пятно. Circuit breaker и повторы добавили, а метрик «сколько повторов», «в каком состоянии размыкатель» и «сколько трафика сбросили» нет; в инциденте не видно, работают ли они вообще, а на ревью не понять, помогают они или маскируют настоящую проблему. Каждый паттерн заслуживает отдельного SLI и панели на дашборде. Дисциплина базовая. Откладывают её постоянно.

Про границу. Всё это окупается там, где у сервиса есть сетевые зависимости и заметный трафик. Для внутренней утилиты, которая раз в час ходит в одну базу, полный набор паттернов — лишний код, который сам становится источником багов: неверно настроенный circuit breaker роняет вызовы, которые прошли бы. И обратная сторона общая для всех этих механизмов: они прячут симптом. Сервис с идеальными повторами и деградацией месяцами живёт поверх зависимости, которая отвечает через раз, — пока об этом не узнают из счёта за трафик или из жалобы на «иногда медленно».

  • Capacity Planning — эти паттерны закрывают ситуацию, когда мощности кончились или прогноз ошибся. Деградация и сброс нагрузки — то, что происходит, пока масштабирование догоняет. Если догонит.
  • SLO Engineering — паттерны держат цель под нагрузкой и при отказах. Circuit breaker не даёт бюджету ошибок сгореть на каскаде.
  • Networking — таймауты, повторы и размыкание латают ненадёжность сети. Service mesh реализует часть этого на инфраструктурном слое.
  • SLI-based Alerting — алерты ловят момент срабатывания: цепь разомкнулась, повторы выросли, трафик начали сбрасывать.
  • Chaos Engineering — эксперимент проверяет, что механизмы живые: circuit breaker правда открывается? повторы не усиливают шторм? отсеки изолируют?
  • Progressive Delivery — канареечная выкатка с проверкой здоровья опирается на пробы readiness и метрики размыкателя.
  • Operating Systems — состояние на уровне системы (открытые файловые дескрипторы, таблица conntrack, давление на страничный кеш) часто и есть то, на что реагирует деградация.
  • Service Mesh — прокси рядом с сервисом умеет размыкание, отсев неисправных адресатов и политики повторов без правок кода; это одна из инфраструктурных реализаций паттернов отсюда.
  • Containerization & OrchestrationPodDisruptionBudget, автомасштабирование, распределение по зонам, пробы liveness и readiness — родные для k8s реализации тех же идей.
  • Idempotency Patterns (TBD) — отдельный лист: ключи идемпотентности, ETag, условные записи, исходящий журнал транзакций, иллюзия доставки ровно один раз.
  • Backpressure & Load Shedding (TBD) — деградация в сторону источника нагрузки: классификация по критичности, контроль допуска, управление очередями, бюджеты повторов.
  • SLI для самих паттернов — что брать за показатель: долю времени в разомкнутом состоянии, успешность повторов, долю сброшенного трафика?