Netflix Hystrixが開発終了したのでResilience4jに移行

##環境
- Spring Boot 2.1.0.RELEASE
- Java 8

##Netflix Hystrix
https://github.com/Netflix/Hystrix
↑いわく、最後のコミットをしたよ、とのこと。
脆弱性とかバグとかが見つかる前に移行します。

##resilience4j
###ソース
https://github.com/resilience4j/resilience4j

###POM
```xml:pom.xml
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>0.17.0</version>
</dependency>
```

###リファレンス
https://resilience4j.readme.io/docs


##移行前のソース


```java:MyContoller.java
// Contollerの修正はなし
@Slf4j
@Controller
@RequestMapping("/example")
@RequiredArgsConstructor
public class MyController {

    @NonNull
    MyService service;

    @PostMapping("/post")
    public String doPostForm() throws Exception {
        if(service.registration()) {
           return "成功時の遷移先";
        }
        return "失敗時の遷移先";
    }
｝


```

```java:MyService.java
public class MyService {

    @Transactional(readOnly = false)
    // CircuitBreakerの設定
    @HystrixCommand(fallbackMethod = "fallback", commandProperties = {
            @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "10000"),
            @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")})
    public boolean registration(MyParameter param,
            String) throws Exception {
        // connect to external api...
        return true;
    }
    
    /**
     * fallback時に実行
     */
    public boolean fallback(Throwable t) {
        log.warn("circuit breaker open", t);
        return false;
    }
}
```

##移行後のソース

```yml:application.yml
resilience4j:
  circuitbreaker:
    configs:
        default:
            registerHealthIndicator: true
            ringBufferSizeInClosedState: 10
            ringBufferSizeInHalfOpenState: 3
            automaticTransitionFromOpenToHalfOpenEnabled: true
            waitDurationInOpenState: 2s
            failureRateThreshold: 50
            eventConsumerBufferSize: 10
    instances:
        myCircuitBreaker:
            baseConfig: default
```

```java:MyService.java
// ~~ importは省略 ~~
import io.vavr.control.Try;
public class MyService {

    CircuitBreaker circuitBreaker;

    @NonNull
    CircuitBreakerRegistry registry;

    @PostConstruct
    public void init() {
        circuitBreaker = registry.circuitBreaker("myCircuitBreaker");
    }

    @Transactional(readOnly = false)
    public boolean registration(MyParameter param) throws Exception {
        return Try.ofSupplier(CircuitBreaker.decorateSupplier(circuitBreaker,
                MyStreamHelper.throwingSupplier(() -> {
                    // connect to external api...
                    return true
                })))
                // Circuit Breaker Open時に実行する処理
                .recover(throwable -> {
                    return fallback(throwable);
                }).get();
    }

    /**
     * fallback時に実行
     */
    private boolean fallback(Throwable t) {
        log.warn("circuit breaker open", t);
        return false;
    }
}
```

## 補足
`MyStreamHelper`については下記をご参照ください。
try-catchを隠しているだけです。
https://shironeko.hateblo.jp/entry/2016/07/16/140236


##2020/07/10追記
###ymlではなくJavaに設定値を書く場合

```java

// Bean登録する場合の書き方。
// 利用する側はcircuitBreakerをDIすることでmyCircuitBreakerが利用できる。
@Bean
  public CircuitBreaker circuitBreaker() {
	  CircuitBreakerConfig config = CircuitBreakerConfig.custom()
              // ymlに書いていた値をここに書いていく
              .failureRateThreshold(20)
              .build();
	  return CircuitBreakerRegistry.of(config).circuitBreaker("myCircuitBreaker");
  }

```
参考
https://www.codeflow.site/ja/article/resilience4j