petitviolet blog

    AkkaのCircuitBreakerとNetflixのHystrixの違い

    2016-12-03

    QiitaJavaScalaAkka

    この記事はなに?

    マイクロサービス等の文脈で登場する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
    )
    

    from: https://qiita.com/petitviolet/items/f1975fba06655a95e3b0