Help us understand the problem. What is going on with this article?

AkkaのCircuitBreakerとNetflixのHystrixの違い

More than 3 years have passed since last update.

この記事はなに?

マイクロサービス等の文脈で登場するCircuitBreaker
Scalaで使えるものの代表(要出典)として以下の2つを比較する。

ちなみに個人的な好みはAkkaの方。
Scalaから使いやすいのと導入が簡単。

CircuitBreakerってどんなもの?

以前書いたので貼っておきます。
[Akka]CircuitBreakerはどう動くのか - Qiita

AkkaのCircuitBreaker

公式サンプルがある。
ちなみに独自のサンプル実装はここ。

akka.pattern.CircuitBreakerに状態遷移の条件に当たるもの(maxFailures, callTimeout, resetTimeout)を渡してインスタンス化する。
そしてCircuitBreaker#withCircuitBreakerscala.concurrent.Futureを引数として与えるだけ。
一行抜粋するとこんな感じ。

breaker.withCircuitBreaker(Future(dangerousCall)) pipeTo sender()

withCircuitBreakerFutureの実行を監視し、成功したら結果をFutureとして取得する。
規定回失敗したらOpenに遷移して...というCircuitBreakerらしい機能を提供している。

これの嬉しい点は引数としてscala.concurrent.Futureを要求するところで、監視対象の処理はAkkaやActorには依存しない。
CircuitBreakerのコンストラクタにakka.actor.Schedulerを要求する程度。

機能もシンプルなCircuitBreakerという程度で、Futureの監視以外は何も提供されていない。
ThreadPoolはコンストラクタのExecutionContextとして設定することになる。

Hystrix

公式サンプルはここ。
独自なサンプル実装はここ。

AkkaのCircuitBreakerと違って、実行を監視する存在がいるのではなく、監視対象となる処理自体をHystrixCommandとして定義・実装する点が大きく違う。
一行抜粋するとこんな感じ。

String s = new CommandHelloWorld("Bob").execute();

execute以外にもjava.util.concurrent.Futureを返却するqueue()rx.Observableを返却するobserver()がある。

Future<String> s = new CommandHelloWorld("Bob").queue();
Observable<String> s = new CommandHelloWorld("Bob").observe();

getFallbackを定義しておくことでexecute等が失敗した際のfallback値を返却する機能もある。

処理そのものをHystrixCommandとして定義するのはすっきりして良い。
外部からはexecuteのような処理のkickしか出来ないので、必要な情報を詰め込んでおける。

ここでは詳しくあげないが、Command実行時のMetricsを取得できたりダッシュボードが用意してあったりキャッシュも組み込んであったりと色々出来る。
高機能過ぎる...。

あと、そもそもCircuitBreakerで監視したい対象は、外部サービスを非同期に呼び出す処理だったりすることが多い。
なので非同期呼び出し前提としてexecuteは無くても良さそう。

CircuitBreakerとしてのHystrix

機能が盛り沢山なHystrixだが、今回はCircuitBreakerとして見てみる。
設定出来る項目は以下を参照。
Configuration · Netflix/Hystrix Wiki

実際に設定を適用するにはこんな感じ。

object MyCommand {
  val key = HystrixCommandGroupKey.Factory.asKey("my-command")

  private val circuitBreakerSetter =
    HystrixCommandProperties.Setter()
      .withCircuitBreakerEnabled(true)
      .withCircuitBreakerRequestVolumeThreshold(10)
      .withCircuitBreakerErrorThresholdPercentage(30)
      .withCircuitBreakerSleepWindowInMilliseconds(50)

  val setter: HystrixCommand.Setter =
    HystrixCommand.Setter
      .withGroupKey(key)
      .andCommandPropertiesDefaults(circuitBreakerSetter)
}

class MyCommand(num: Long) extends HystrixCommand[Result](MyCommand.setter) {
  ???
}

Akkaの何回失敗したら、という設定と異なり、一定時間内にどの程度(%)失敗したらOpenになる、という設定の仕方。

ざっくり比較

AkkaのCircuitBreakerは非常にシンプルだが機能は少ない。
繰り返しになるが単なるscala.concurrent.Futureを監視出来る手軽さは強力かなと。
設定出来る項目も少なく、学習コストは低いと思われる。
導入も捨てるのも簡単なので総合的にお手軽。

それに対してHystrixは高機能だがやや複雑な印象。
ダッシュボードやキャッシュといった機能だけでなくThreadPoolやタイムアウトの設定なども細かく出来る。
使い込めばかなり便利そう。
もちろん、単純にHystrixCommandを実装したコマンドを使うだけなら簡単。

とりあえずCircuitBreakerとしての機能が必要ならAkkaで、
ダッシュボード等の機能も必要であればHystrixを使う。

ちなみにAkkaの方でもこんなIssueが立ってたりで面白い。
CircuitBreaker should provide statistics like Netflix Hystrix does · Issue #16617 · akka/akka

Scala的な観点

あまり言いたくないがHystrixの難点はJavaであることで、Scalaから使うという観点に立つとObservableはともかくjava.util.concurrent.Futureは使いづらい。
scala.concurrent.Promise使うとかjava.util.concurrent.Future#getscala.concurrent.Future#applyで非同期にするとか...?

多少頑張ればScalaからでもいい感じに使えそう。
HystrixをScala / Playアプリケーションから使ってみる - たけぞう瀕死ブログ

ちなみにObservableScala2.12のSAMのおかげで以下のように簡潔に使えるようになっているのが嬉しい。

command.observe().subscribe(
  (r: Result) => doSomething(r), // onNext
  (t: Throwable) => t.printStackTrace(),  // onError
  () => println("complete")  // onComplete
)
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away