Что такое паттерн circuit breaker и как он работает
Что проверяют
Знаете ли вы три состояния и понимаете ли, от чего именно защищает приём.
Как отвечать
Предохранитель защищает не отказавшую зависимость, а вызывающий сервис. Когда сосед лёг, каждый обращённый к нему запрос висит до таймаута, занимая поток и соединение. Через несколько секунд пул исчерпан, и падает уже ваш сервис, а за ним следующий. Предохранитель разрывает эту цепочку.
Три состояния:
- замкнуто: вызовы проходят, доля ошибок и медленных ответов считается в скользящем окне;
- разомкнуто: порог превышен, и вызовы отклоняются мгновенно, без попытки сходить по сети. Зависимость получает передышку, а вызывающий не копит зависшие потоки. Держится заданное время;
- полуразомкнуто: пропускается небольшое число пробных вызовов. Успешны, значит возвращаемся в замкнутое; нет, значит снова размыкаем.
Настраивается порогом доли ошибок, размером окна, порогом медленных вызовов, длительностью ожидания и числом пробных вызовов. В экосистеме Java это Resilience4j, пришедший на смену закрытому Hystrix.
Рядом обязательно должны стоять другие приёмы, иначе толку мало:
- таймаут на каждом внешнем вызове, причём меньший, чем у вызывающего вас;
- повторы с экспоненциальной задержкой и разбросом, и обязательно ограниченные. Повторы без предохранителя усиливают аварию: сервис лежит, а вы утраиваете нагрузку на него;
- переборка (bulkhead): отдельный пул на каждую зависимость, чтобы одна не съела все потоки;
- запасной ответ: отдать кэш, урезанные данные, понятную ошибку. Осмысленная деградация вместо пятисотой.
Важно, что открытый предохранитель это не тишина: он должен быть виден в метриках и в оповещениях, иначе деградация останется незамеченной.
Где спотыкаются
Ставят повторы без предохранителя и добивают соседа.
Не задают таймаут, из-за чего до порога дело не доходит.
Один предохранитель на все зависимости сразу.
Молча отдают пустой ответ, и сбой не виден в мониторинге.