Lagomを試してみるシリーズ第8回です。今回はLagomの特徴的な機能の1つであるサーキットブレーカーを試してみました。
マイクロサービスのベストプラクティスのひとつに障害の発生したサービスをシステムから切り離すことでサービス全体を停止せずに運用するためのサーキットブレーカーの導入があります。サーキットブレーカーの機能を持つプロダクトとしてはNetflixのHystrixなどが有名ですが、Lagomはこの機能をフレームワークとして提供しており、ちょっとしたコードを記述するだけで簡単に使用できてしまいます。
まずはLagomのサーキットブレーカーの動きについて説明しておきます。サーキットブレーカーは以下の3つの状態を持ちます。
- Closed: サーキットブレーカーが閉じている、つまりサービスが問題なく稼働しておりアクティブな状態です。一定回数失敗するとOpen状態に移行します。
- Open: サーキットブレーカーが開放されている、つまりサービスに障害があり、切り離されている状態です。Open状態から一定時間経過するとHalf-open状態に移行します。
- Half-open: サービスの再開にトライしている状態です。サービスが正常な状態に戻ればそのままClosed状態に、引き続き失敗する場合はOpen状態に戻ります。
なお、Lagomのサーキットブレーカーはクライアント側で実装されているため、前回紹介したサービスクライアントを使用してサービスを呼び出さないと使用することができません。
サーキットブレーカーを使用するにはサービスの定義でwithCircuitBreaker
を指定します。サーキットブレーカーの設定はIDごとに行うことができますのでバックエンドを共有しているサービスなど、障害時にまとめて切り離したい単位でIDを振っておくとよいでしょう。
@Override default Descriptor descriptor() { // @formatter:off return named("helloservice").with( restCall(Method.GET, "/api/hello", hello()) .withCircuitBreaker(new Descriptor.CircuitBreakerId("hello")) ).withAutoAcl(true); // @formatter:on }
このサービスを呼び出すサービスの実装プロジェクトのapplication.conf
に以下のような設定を追加します。
lagom.circuit-breaker { default { # サーキットブレーカーを有効にする場合はonにする enabled = on # 指定した回数呼び出しに失敗したらOpen状態に移行する max-failures = 10 # 呼び出し失敗とみなすタイムアウト時間 call-timeout = 10s # Open状態からHalf-open状態に移行するまでの時間 reset-timeout = 15s } }
以下のようにサーキットブレーカーのIDごとに設定を上書きすることができます。
lagom.circuit-breaker { # デフォルトの設定 default { enabled = on max-failures = 10 call-timeout = 10s reset-timeout = 15s } # helloというIDのサーキットブレーカーの設定 hello { enabled = on max-failures = 2 call-timeout = 1s reset-timeout = 10s } }
実際の動きを見てみます。以下のようにサービスの呼び出しが設定した回数失敗すると「Circuit breaker [hello] open」というメッセージが表示され、サーキットブレーカーが開放されていることがわかります。
[error] myservice - Exception in RestCallId{method=GET, pathPattern='/api/hoge'} akka.pattern.CircuitBreaker$$anon$1: Circuit Breaker Timed out. [error] myservice - Exception in RestCallId{method=GET, pathPattern='/api/hoge'} akka.pattern.CircuitBreaker$$anon$1: Circuit Breaker Timed out. [warn] c.l.l.i.c.CircuitBreakerMetricsImpl - Circuit breaker [hello] open
この状態でサービスを呼び出そうとすると以下のように即座にエラーが返ってきます。
[error] myservice - Exception in RestCallId{method=GET, pathPattern='/api/hoge'} akka.pattern.CircuitBreakerOpenException: Circuit Breaker is open; calls are failing fast
reset-timeout
で設定した時間が経過した後にサービスを呼び出すと再度サービスの呼び出しにトライし、その時点でサービスが正常にレスポンスを返すことができればサーキットブレーカーはClosed状態に戻ります。
Lagomで完結していないと機能しないのが残念なところですが、別途ミドルウェアを導入しなくても簡単にサーキットブレーカーを利用できるのは便利ですね。仕組み的にLagom以外からの呼び出しに対応できないのは仕方ないですが、Lagomから外部のサービスを呼び出すときは使えそうな気がするので内部のAPIがどんな感じになっているのか気になるところです。