Skip to content

Instantly share code, notes, and snippets.

@Toparvion
Created June 4, 2021 09:37
Show Gist options
  • Save Toparvion/a936120eb9fcd3939a4ac778c1ffcb9b to your computer and use it in GitHub Desktop.
Save Toparvion/a936120eb9fcd3939a4ac778c1ffcb9b to your computer and use it in GitHub Desktop.
Описание работы и параметров CircuitBreaker'а в Resilience4J

В случае с Resilience4J предохранитель работает как конечный автомат с тремя нормальными состояниями:

и двумя специальными: DISABLED & FORCED_OPEN.

Дальше будет описана логика работы предохранителя в привязке к его конфигурируемым параметрам.

Предохранитель постоянно анализирует результаты проходящих через него вызовов и накапливает их в т.н. скользящем окне. Окна бывают двух типов в зависимости от параметра slidingWindowType:

  • COUNT_BASED (по количеству): анализируются последние slidingWindowSize штук вызовов за всё последнее время;
  • TIME_BASED (по времени): анализируются все вызовы за последние slidingWindowSize секунд времени.

Чтобы предохранитель не начал делать выводы слишком рано, ему можно задать минимальное количество вызовов minimumNumberOfCalls, необходимых для накопления статистики. Соответственно, пока такое число вызовов не будет накоплено, предохранитель не сработает, даже если все эти запросы были неуспешными или медленными (см. ниже).

При добавлении нового результата вызова в окно, предохранитель каждый раз пересчитывает 2 показателя:

  1. Процент неуспешных вызовов (по отношению к общему числу вызовов в окне). Неуспешными по умолчанию считаются вообще любые исключения, однако их список можно сузить/расширить параметрами recordException(s) и ignoreException(s) (последний позволяет вообще никак не учитывать исключение: ни как ошибку, ни как успех);
  2. Процент медленных вызовов (по отношению к общему числу вызовов в окне). Медленными считаются вызовы, выполнение которых занимает более slowCallDurationThreshold мс. Этот показатель призван превентивно снять нагрузку с деградирующего маршрута, т.е. еще до того, как он окончательно “лёг” и стал отвечать откровенными ошибками.

Достижение любым из этих показателей соответствующего порогового значения заставляет предохранитель “разорвать цепь”, то есть перейти в состояние OPEN. Для неуспешных запросов пороговым значением является процент failureRateThreshold, а для медленных – процент slowCallRateThreshold.

Когда предохранитель открыт, на все запросы он мгновенно отвечает исключением CallNotPermittedException. Выйти из этого состояния он может лишь спустя не менее, чем waitDurationInOpenState мс. При этом, если automaticTransitionFromOpenToHalfOpenEnabled выставлен в true, то выход из состояния OPEN произойдёт сам через заданное время (за счёт отдельного потока наблюдателя), а если false, то триггером должен послужить какой-либо запрос. Как следствие, при false предохранитель может остаться открытым дольше указанного времени.

После открытия предохранитель не закрывается сразу; сначала он переходит в промежуточное состояние HALF_OPEN, чтобы “прощупать” защищаемый маршрут и понять, можно ли уже возвращать на него полную нагрузку, или ещё рано. В этом состоянии он допускает выполнение permittedNumberOfCallsInHalfOpenState вызовов, вычисляет для них доли успешных и долгих, сравнивает их с заданными порогами и на основе этого сравнения либо открывается снова (OPEN), либо закрывается (CLOSED). Поскольку запросов может оказаться мало для накопления такой статистики, у предохранителя предусмотрен таймаут maxWaitDurationInHalfOpenState мс, по истечении которого он вернется в состояние OPEN. По умолчанию таймаут равен 0, то есть предохранитель готов ждать в этом состоянии вечно. Если за время нахождения в полуоткрытом состоянии успевает накопиться permittedNumberOfCallsInHalfOpenState вызовов, и они ещё в процессе выполнения, то все новые вызовы также получат CallNotPermittedException.

Пример срабатывания предохранителя после трёх неуспешных попыток вызова какой-либо функции одним и тем же потоком:

Входить в специальные состояния DISABLED & FORCED_OPEN и выходить из них предохранитель может только принудительно. Это может быть полезно для ручного управления отдельными контурами, например, через JMX.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment