Опирается на правила: R-RES-CFG-1R-RES-CFG-3 и R-RES-CFG-X1 из Resilience Style Guide → раздел 8. Конфигурация.

Важно знать

  • Конфиг Resilience4j — через application.yml (declarative), не через @Bean CustomCircuitBreakerConfig.
  • Это позволяет менять параметры через Spring Cloud Config / Vault без redeploy.
  • Defaults в секции configs.default, переопределения per-instance через base-config: default.
  • Имена instances совпадают с именами beans и систем: sber, odnakassa, insurance, receipt.
  • Структура одинакова для circuitbreaker, bulkhead, retry, timelimiterconfigs.default + instances.<name>.
  • Программная конфигурация через CircuitBreakerConfig.custom() — только если действительно нужна логика, которой нет в declarative (редко).

Resilience4j-параметры — это operational knobs: их подкручивают на основе реального поведения в проде. Если они лежат в Java-коде, любое изменение требует пересборки и редеплоя. Если в application.yml — меняются конфиг-сервером, без выкатки. Раскрытие раздела 8 гайда.

Declarative через application.yml

R-RES-CFG-1: вся конфигурация — в yml.

resilience4j:
  circuitbreaker:
    configs:
      default:
        sliding-window-type: COUNT_BASED
        sliding-window-size: 50
        minimum-number-of-calls: 10
        failure-rate-threshold: 50
        wait-duration-in-open-state: 30s
        permitted-number-of-calls-in-half-open-state: 3
        slow-call-rate-threshold: 50
        register-health-indicator: true
    instances:
      sber:
        base-config: default
        failure-rate-threshold: 30          # критичная система — порог ниже
        slow-call-duration-threshold: 15s
      odnakassa:
        base-config: default
        slow-call-duration-threshold: 30s
      insurance:
        base-config: default

  bulkhead:
    configs:
      default:
        max-concurrent-calls: 10
        max-wait-duration: 100ms
    instances:
      sber:
        max-concurrent-calls: 16
      odnakassa:
        max-concurrent-calls: 24
      insurance:
        max-concurrent-calls: 8

  retry:
    configs:
      default:
        max-attempts: 3
        wait-duration: 500ms
        enable-exponential-backoff: true
        exponential-backoff-multiplier: 2.0
        retry-exceptions:
          - java.io.IOException
          - org.springframework.web.client.HttpServerErrorException
        ignore-exceptions:
          - org.springframework.web.client.HttpClientErrorException
    instances:
      sber:
        base-config: default
      odnakassa:
        base-config: default

Что важно:

  • configs.default — наследуемые параметры.
  • instances.<name> — фактические инстансы; base-config: default берёт всё из default, дальнейшие поля переопределяют.
  • Параметры с обоснованием: например, slow-call-duration-threshold: 15s для sber — потому что readTimeout: 30s и порог /2 (см. Circuit Breaker).

Defaults vs per-instance

R-RES-CFG-2: всё общее — в configs.default. Per-instance только то, что реально отличается.

resilience4j.circuitbreaker.configs.default:
  sliding-window-size: 50
  failure-rate-threshold: 50
  # ... все типовые параметры

resilience4j.circuitbreaker.instances:
  sber:
    base-config: default
    failure-rate-threshold: 30          # ← только то, что отличается
  odnakassa:
    base-config: default                # ← всё стандартное

Зачем:

  • При появлении новой системы — одна строка <name>: base-config: default, и она получает все типовые настройки.
  • При изменении общего параметра (например, повысили sliding-window-size до 100) — правка в одном месте.
  • Per-instance переопределения сразу видны как отклонения от стандарта — это сигнал, что нужно их обосновать.

Имена instances — same as system

R-RES-CFG-3: имя инстанса в yml = имя bean'а = имя системы. Совпадение важно для R-RES-ISO-3.

resilience4j.circuitbreaker.instances.sber:        # ← name = "sber"
  base-config: default
@Bean("sberOkHttpClient")                          // ← bean name содержит "sber"
OkHttpClient sberOkHttpClient(...) { ... }

@CircuitBreaker(name = "sber", ...)                // ← same "sber"
public RegisterResult register(...) { ... }

Имена короткие, нижний регистр, без хост-частей. sber, не sber-payment-prod-eu-west-1.

Интеграция со Spring Cloud Config

Главная выгода declarative-подхода: параметры можно менять runtime.

# в Spring Cloud Config Server / Vault
resilience4j.circuitbreaker.instances.sber:
  failure-rate-threshold: 20      # снизили в проде в инцидент

После POST /actuator/refresh сервис подхватит новые значения без перезапуска. Это критично:

  • В инцидент SRE снижает failure-rate-threshold чтобы CB открывался раньше.
  • Подкручивают sliding-window-size в зависимости от нагрузки.
  • Меняют slow-call-duration-threshold при появлении деградации внешней системы.

Без declarative каждое изменение — pull request, code review, build, deploy. 30 минут на «правку числа». С declarative — 30 секунд.

Что запрещено

Программная конфигурация через CircuitBreakerConfig.custom()

R-RES-CFG-X1: создание CircuitBreakerConfig руками — скрытая конфигурация.

// ПЛОХО — программный конфиг
@Bean
CircuitBreaker sberCircuitBreaker() {
    CircuitBreakerConfig config = CircuitBreakerConfig.custom()
        .slidingWindowSize(50)
        .failureRateThreshold(30)
        .waitDurationInOpenState(Duration.ofSeconds(30))
        .build();
    return CircuitBreaker.of("sber", config);
}

Что не так:

  • Не управляется через Cloud Config. В инцидент SRE не может поменять failureRateThreshold без выкатки.
  • Параметры в двух местах. Если рядом есть application.yml-конфиг для CB, программный его не учтёт; кто-то будет смотреть в yml и недоумевать, почему не работает.
  • @CircuitBreaker annotation работает по другому пути. Программно созданный CB надо вручную обернуть им код (circuitBreaker.executeSupplier(...)), теряется declarative-стиль аннотаций.

Когда это всё-таки оправдано: очень редко, если нужна логика, которой нет в yml (custom recordException, dynamic threshold). Тогда — отдельный @Configuration с комментарием, объясняющим, почему declarative не подходит.

Что запрещено — таблица

АнтипаттернПравилоЧто взамен
Программный CircuitBreakerConfig.custom() без причиныR-RES-CFG-X1application.yml declarative
Дублирование параметров в каждом instance без base-config: defaultR-RES-CFG-2Default + переопределения
Длинные хост-имена для instancesR-RES-CFG-3Короткие: sber, odnakassa
Разные имена для CB / Bulkhead / Retry одной системыR-RES-CFG-3Единое имя
Конфиг в @Bean instead of ymlR-RES-CFG-1yml + Cloud Config

Куда дальше

  • Resilience → раздел 8. Конфигурация — нормативные R-RES-CFG-*.
  • Per-system isolation — почему имя бin'а и инстансов совпадают.
  • Circuit Breaker — параметры sliding window и threshold.
  • Bulkhead — sizing maxConcurrentCalls.
  • Retry — max-attempts, wait-duration, retry/ignore exceptions.
  • Observability — Micrometer-метрики для tuning.
  • Validation → Configuration validation — fail-fast невалидной конфигурации.