LoginSignup
3
5

More than 5 years have passed since last update.

Spring BootでCircuit Breaker(Hystrix)を使ってみる

Last updated at Posted at 2017-12-18

Cicuit Breakerは、マイクロサービスで一部のサービスが落ちたときに、障害が連鎖しないようアクセスを遮断して切り離すための仕組み。
Circuit Breakerに関して、詳しくはここ

今回はHystrixをSpringから使ってみます。

環境

Java 8
Spring Boot 1.5.7.RELEASE

まずやってみる

サンプルアプリの概要

クライアントからFrontendAPI(localhost:8090/hello)を叩くと現在時刻が返ってくるシンプルなAPIです。
FrontendAPIは内部でさらにBackendAPI(localhost:8080/time)を叩いて現在時刻を取得する仕組みです。
今回はここにHystrixを導入して、BackendAPIに障害が発生したときも、FrontendAPIに障害が伝搬せずに継続できるような仕組みを作ってみます。

image.png

  • Backend API
    • 概要:現在時刻を返すだけ
    • ホスト:localhost:8080
    • エンドポイント:/time
  • Frontend API
    • 概要:アプリケーション1を叩いて返ってきた内容をそのまま返す
      • app1が落ちている or 3秒以上レスポンスが返ってこなかったら、保持しておいた最後に成功したときのレスポンスを返す (ここにHystrixを使う)
    • ホスト:localhost:8090
    • エンドポイント:/hello

作る

まずBackendAPI。
Spring InitializerでWebにチェックを入れてプロジェクト作成。
BackendControllerを下記の内容で作成。

@RestController
@RequestMapping("/time")
public class BackendController {

    @GetMapping
    public String app1() throws InterruptedException {
        String now = LocalDateTime.now().toString();
        return now;
    }
}

次にFrontendAPI。
Spring InitializerでWeb, lombok, Hystrixにチェックを入れてプロジェクト作成。
FrontendServiceを以下の内容で作成。
オプションなどの説明はコメントに書いた。

@Service
public class FrontendService {

    private String lastResponse = "error";

    @HystrixCommand(
            // エラー時、もしくはサーキットがOPEN時に代わりに呼ばれるメソッド
            fallbackMethod = "fallback",
            commandProperties = {
                // サーキットがオープンになるエラー回数。デフォルトは20
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
                // サーキットがオープンになった後、指定したミリ秒待って再度リクエストを受け付ける。再度リクエストして成功すればサーキットがクローズする。デフォルトは5000
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),
                // 指定した時間レスポンスが返ってこなかったらエラーとして処理する
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
                // サーキットがオープンになるエラー率。デフォルトは50%. 
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
            }
    )
    public String get() {
        System.out.println("call time api.");

        RestTemplate restTemplate = new RestTemplate();
        URI uri = URI.create("http://localhost:8080/time");
        String response = restTemplate.getForObject(uri, String.class);

        // lastResponseフィールドにレスポンスを保持する
        lastResponse = response;

        return response;
    }

    public String fallback() {
        // 最後に成功したときのレスポンスを返す
        return lastResponse;
    }
}

FrontendControllerを下記の内容で作成

@RestController
@RequestMapping("/hello")
@RequiredArgsConstructor
@EnableCircuitBreaker
public class FrontendController {

    private final FrontendService service;

    @GetMapping
    public String hello() {
        return service.get();
    }
}

動かしてみる

両方のAPIを動かした状態で、FrontendAPIを叩く

$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345

現在時刻が返ってくる。

アプリケーションのコンソールには下記のログが出ている。

call time api.

では、BackendAPIを落とした状態で10回くらい叩いてみる

$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345
$ curl "http://localhost:8090/hello"
2017-12-02T15:53:02.345

10回叩いても同じ時刻が返ってきている。BackendAPIが落ちているので、fallbackメソッドが呼ばれて、最後に成功したときのレスポンスが返っている。
ログを見てみるとBackendAPIを5回叩いた形跡が見られる。

call time api.
fallback.
call time api.
fallback.
call time api.
fallback.
call time api.
fallback.
call time api.
fallback.
fallback.
fallback.
fallback.
fallback.
fallback.

これは@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),で設定したとおり、5回連続でエラーが返ったその時点でサーキットがオープンになり、その後はBackendAPIは叩かれずにfallbackが呼ばれている。

今度はBackendAPIに3秒間のスリープ処理(Thread.sleep(3000))を入れて、わざとタイムアウトするようにしてみます。
また10回ほど叩いてみます。

$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.385
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.385
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.385
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.745
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.745
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.745
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.745
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.745
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.745
$ curl "http://localhost:8090/hello"
2017-12-02T15:55:02.745

1~3回目までは最後に成功したときの時間が返っています。
4回目は、ちょうど1回目に送ったリクエストが返ってきて、キャッシュの値が更新されたのか、若干時刻が新しくなっています。
ログを見てみると先程と同様に6回目からBackendAPIを叩かなくなっています。

call time api.
fallback.
call time api.
fallback.
call time api.
fallback.
call time api.
fallback.
call time api.
fallback.
fallback.
fallback.
fallback.
fallback.
fallback.

所感

  • オプション等はここが詳しい
  • Hystrixの設定、application.ymlに書きたい

参考

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5