Spring Boot + Resilience4j で CircuitBreaker と RateLimiter を試す（続き）

前回まとめきれていなかった、イベントとモニタリングについて書いていきます。

前回の記事はこちら。
[Spring Boot + Resilience4j で CircuitBreaker と RateLimiter を試す](https://qiita.com/d-yosh/items/31fad6b4f6a9bb518e6d)

# 環境
* JDK 8
* Spring Boot 2.1.2.RELEASE
* Resilience4j 0.13.2

#CircuitBreaker

## イベント

CircuitBreaker は以下のイベントを発行します。

| イベント | 内容
|---|---|
| Success | 処理が成功したとき
| Error | 処理が失敗したとき
| StateTransition | Circuit の状態が遷移したとき
| Reset | CircuitBreaker の状態をリセットしたとき
| IgnoreError | 無視すると設定した例外が発生したとき
| CallNotPermitted | Circuit が Open 状態でアクセスが遮断されたとき

これらのイベントに対して処理を行う場合には、以下のように設定できます。

```java
@Configuration
@Slf4j
public class CircuitBreakerConfiguration {

    private final CircuitBreakerRegistry circuitBreakerRegistry;

    public CircuitBreakerConfiguration(CircuitBreakerRegistry circuitBreakerRegistry) {
        this.circuitBreakerRegistry = circuitBreakerRegistry;
    }

    @PostConstruct
    public void eventSetting() {
        circuitBreakerRegistry.getAllCircuitBreakers().forEach(circuitBreaker -> {
            circuitBreaker.getEventPublisher().onSuccess(e -> log.info("SUCESS!!!!!"))
                    .onError(e -> log.error("ERROR!!!!!"))
                    .onStateTransition(e -> log.info("{} to {}", e.getStateTransition().getFromState(),
                            e.getStateTransition().getToState()))
                    .onCallNotPermitted(e -> log.warn("Not Permitted!!!!!"))
                    .onIgnoredError(e -> log.info("Ignored!!!!!")).onReset(e -> log.info("Reset!!!!"));
        });
    }
}
```

CircuitBreaker から `getEventPublisher`で取得したオブジェクトに対して、`on〜`メソッドで処理を設定できます。
また、`onEvent`メソッドを利用すれば、すべてのイベントに対して処理を設定できます。

## モニタリング

Spring Boot Actuator で CircuitBreaker の情報を取得することができます。

### Endpoint

`circuitbreakers`という Endpoint にアクセスすると、登録されている CircuitBreaker の名称を取得できます。

```
$ curl http://localhost:8080/actuator/circuitbreakers
{"circuitBreakers":["circuitA","circuitB"]}
```

`circuitbreaker-events`という Endpoint にアクセスすると、各 CircuitBreaker が発行したイベントを一覧で取得できます。

```
$ curl http://localhost:18080/actuator/circuitbreaker-events
{"circuitBreakerEvents":[{"circuitBreakerName":"circuitA","type":"ERROR","creationTime":"2019-01-24T10:02:21.265+09:00[Asia/Tokyo]","errorMessage":"java.lang.RuntimeException","durationInMs":4},{"circuitBreakerName":"circuitA","type":"SUCCESS","creationTime":"2019-01-24T10:02:40.239+09:00[Asia/Tokyo]","durationInMs":0},{"circuitBreakerName":"circuitA","type":"SUCCESS","creationTime":"2019-01-24T10:02:41.599+09:00[Asia/Tokyo]","durationInMs":0},{"circuitBreakerName":"circuitA","type":"ERROR","creationTime":"2019-01-24T10:02:42.754+09:00[Asia/Tokyo]","errorMessage":"java.lang.RuntimeException","durationInMs":0},{"circuitBreakerName":"circuitA","type":"ERROR","creationTime":"2019-01-24T10:02:43.860+09:00[Asia/Tokyo]","errorMessage":"java.lang.RuntimeException","durationInMs":0},{"circuitBreakerName":"circuitA","type":"STATE_TRANSITION","creationTime":"2019-01-24T10:02:43.867+09:00[Asia/Tokyo]","stateTransition":"CLOSED_TO_OPEN"},{"circuitBreakerName":"circuitA","type":"NOT_PERMITTED","creationTime":"2019-01-24T10:02:44.596+09:00[Asia/Tokyo]"},{"circuitBreakerName":"circuitA","type":"STATE_TRANSITION","creationTime":"2019-01-24T10:03:17.660+09:00[Asia/Tokyo]","stateTransition":"OPEN_TO_HALF_OPEN"},{"circuitBreakerName":"circuitA","type":"SUCCESS","creationTime":"2019-01-24T10:03:17.661+09:00[Asia/Tokyo]","durationInMs":0}]}
```

CircuitBreaker の名称と、イベントによって絞り込むことも可能です。
CircuitBrekaer ごとに保存されるイベントの数はデフォルトでは 100 件で、`application.yml`で値を設定することができます。

```yaml
resilience4j:
  circuitbreaker:
    backends:
      circuitA:
        event-consumer-buffer-size: 1000
```

### Health Indicator

各 CircuitBreaker の状態を取得することもできます。
Health Indicator は`{CircuitBreakerの名称}+"CircuitBreaker"` という名前で登録されているので、以下のようにすれば状態を取得できます。

```
$ curl http://localhost:8080/actuator/health/circuitACircuitBreaker
{"status":"UP","details":{"failureRate":"-1.0%","failureRateThreshold":"50.0%","maxBufferedCalls":5,"bufferedCalls":2,"failedCalls":0,"notPermittedCalls":0}}
```

status は Circuit の状態によって決まり、Closed 時は UP、Open の時は DOWN、それ以外の時は UNKNOWN となります。

なお、各 CircuitBreaker はバラバラに登録されているので、CircuitBreaker の情報だけまとめて取得することができなさそうです。
（`CompositeHealthIndicator`でまとめて登録するようになっていればいいのではないかと思わなくもない）
また、デフォルトで有効になっていますが、`application.yml`で無効にすることも可能です。

```yaml
resilience4j:
  circuitbreaker:
    backends:
      circuitA:
        register-health-indicator: false
```

# RateLimiter

CircuitBreaker とほとんど似たような感じです。

## イベント

RateLimiter は以下のイベントを発行します。

|イベント | 内容
|---|---
| Success | 処理が実行できたとき
| Failure | 処理が実行できないとき（タイムアウトしたとき）

イベントに対して処理を設定する方法は CircuitBreaker のときと同様です。

```java
@Configuration
@Slf4j
public class RateLimiterConfiguration {

    private final RateLimiterRegistry rateLimiterRegistry;

    public RateLimiterConfiguration(RateLimiterRegistry rateLimiterRegistry) {
        this.rateLimiterRegistry = rateLimiterRegistry;
    }

    @PostConstruct
    public void eventSetting() {
        rateLimiterRegistry.getAllRateLimiters().forEach(rateLimiter -> {
            rateLimiter.getEventPublisher().onSuccess(e -> log.info("SUCCESS!!!!!!"))
                    .onFailure(e -> log.error("ERROR!!!!!!!"));
        });
    }
}
```

## モニタリング

CircuitBreaker と同じような感じですが、デフォルトの挙動が微妙に異なります。

### Endpoint

`ratelimiters`という Endpoint にアクセスすると、登録されている RateLimiter の名称を取得できます。

```
$ curl http://localhost:8080/actuator/ratelimiters
{"rateLimitersNames":["limiterA","limiterB"]}
```

`ratelimiter-events`という Endpoint にアクセスすると、各 RateLimiter が発行したイベントを一覧で取得できますが、デフォルトでは無効です。
（アクセスできるけど何も情報が蓄積されていない。）
`application.yml`で有効にすることができます。また、CircuitBreaker と同様に保存されるイベントの数を変更することもできます。（デフォルトは 100）

```yaml
ratelimiter:
  limiters:
    limiterA:
      subscribe-for-events: true
      event-consumer-buffer-size: 1000
```

```
$ curl http://localhost:8080/actuator/ratelimiter-events
{"eventsList":[{"rateLimiterName":"limiterA","rateLimiterEventType":"SUCCESSFUL_ACQUIRE","rateLimiterCreationTime":"2019-01-24T11:30:26.068+09:00[Asia/Tokyo]"},{"rateLimiterName":"limiterA","rateLimiterEvenall
tType":"FAILED_ACQUIRE","rateLimiterCreationTime":"2019-01-24T11:30:27.798+09:00[Asia/Tokyo]"}]}
```

### Health Indicator

各 RateLimiter の状態を取得することができます。
Health Indicator は`{RateLimiterの名称}+"RateLimiter"` という名前で登録されているので、以下のようにすれば状態を取得できますが、デフォルトでは無効です。

```
$ curl http://localhost:8080/actuator/health/limiterARateLimiter
{"status":"UP","details":{"availablePermissions":1,"numberOfWaitingThreads":0}}
```

`application.yml`で有効にすることができます。

```yaml
ratelimiter:
  limiters:
    limiterA:
      register-health-indicator: true
```

status は、アクセスしたタイミングでタイムアウトが発生する状態であれば DOWN、正常に処理が可能な状態であれば UP となります。

# 所感

Resilience4j と Spring Boot の連携は若干やりづらい気がしました。
すべての機能に対する AutoConfiguration が揃っていませんし、イベントの登録についても少し面倒であったり、Actuator で取得できる内容や挙動についても気になる部分があります。
ただ、Spring Cloud が Hystrix の移行先として Resilience4j を挙げているので、今後はもっと便利になっていくのだろうと期待しています。