dtyler.io

Microservicesで障害が起きたサービスを切り離すためのCircuit Breaker

2018/11/10

Microservicesのような分散システムでは、各サービスが独立して動いている。(はず。) 仮にどこかのサービスに障害が起こった際も、全体は自律的に動き続けるべきである。

サービスが独立しているということは、別のサービスで障害が起きていることを、すべてのサービスは知らないはずである。 そのため、サービス間通信を行う際は、ネットワーク遅延などに備えリトライ処理を実装すべきだし、 呼び出される側もAPIでRate Limitを実装するべきだ。

しかし、例えばタイムアウト時間を経過するまで多数のリクエストが来続けて、スレッドプールを使い果たし、 システムリソースが枯渇してノードが停止、依存サービスもまとめて死ぬ、ということもあり得る。

このようなケースでシステムはどう動くべきか。タイムアウト時間を短くすればcancelされてスレッドの枯渇は防げるかもしれないが、 正常時のリクエストも失敗しやすくなってしまうかもしれない。 望ましいのは、操作がすぐに失敗し、成功しそうならちゃんと呼び出す、というものだ。

Circuit Breakerパターンは、このような、分散システムでのエラー処理に解決策を提供する。 Circuit Breakerは失敗する可能性のあるリクエストにおけるプロキシとして(実態はサービス内の組み込みロジックであるが) 動作する。つまり、Microservicesにおける各サービスは、自分自身でCircuit Breakerを実装する。 プロキシは、最近発生した障害の数を監視し、その情報を使って、リクエストを送信するか、すぐに失敗させるかを決定する。

Circuit Breakerは、次のような状態を持つState Machineである。

Circuit Breakerの実装で必要なことがいくつかある。

Goでは、go-kit/kit/circuitbreakerや、afex/hystrix-goなどがメジャーなライブラリだ。が、 circuit breaker自体はシンプルな動きだし、自分たちのオペレーションするMicroservicesに組み込みやすいような形のCircuit Breakerを自前実装してもいいと思う。