WildFly Swarmには、Netflix OSSとの統合機能があります。
Stability Patterns / Circuit Breaker
目次のタイトルが「Stability Patterns / Circuit Breaker」なのに、ページのタイトルが「NetflixOSS」というのは
どうなんでしょう?というのはありますが、RibbonやHystrixと統合する機能のようです。
Fault tolerance and resilience | Thorntail
ここではConsulとともに使われているようですが、今回はRibbonもConsulも置いておいて、単純にWildFly Swarmの提供する
Hystrix向けのFraction経由でHystrixを使うことに着目したいと思います。まあ、個人的にちょっとHystrixを把握して
おきたいということがありまして。
Hystrixとは
Netflix OSSの提供する、Circuit Breakerです。
Circuit Breakerとは、主にMicroservicesで外部サービスを呼び出す際に、呼び出し先が障害などでエラーになったりタイムアウトに
なるような時にリクエスト(というか呼び出し)を遮断してしまうものになります。これで、外部サービスの障害に自サービスが引っ張られて
遅延していくといったことを防ぎます。
遮断された呼び出しについては、一定時間後などでまた再開するようになります。もちろん、相手側が復旧していればですが。
Hystrixのドキュメントとしては、こちらを見るとよいでしょう。
WildFly Swarm+Hystrix Fraction
続いて、WildFly SwarmのHystrix向けのFractionについてです。
まず、今回用意したpom.xmlを記載しましょう。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.littlewings</groupId> <artifactId>circuit-breaker-hystrix</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <scala.major.version>2.12</scala.major.version> <scala.version>${scala.major.version}.1</scala.version> <scala.maven.plugin.version>3.2.2</scala.maven.plugin.version> <failOnMissingWebXml>false</failOnMissingWebXml> <wildfly.swarm.version>2017.1.1</wildfly.swarm.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>bom-all</artifactId> <version>${wildfly.swarm.version}</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>${scala.version}</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>jaxrs</artifactId> </dependency> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>hystrix</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>${scala.maven.plugin.version}</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <scalaVersion>${scala.version}</scalaVersion> <args> <arg>-Xlint</arg> <arg>-unchecked</arg> <arg>-deprecation</arg> <arg>-feature</arg> </args> <recompileMode>incremental</recompileMode> </configuration> </plugin> <plugin> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-plugin</artifactId> <version>${wildfly.swarm.version}</version> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
注目点は、ここですね。
<dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>hystrix</artifactId> </dependency>
これでHystrix向けのFractionが追加されるわけですが、Hystrixへのライブラリ依存関係以外に何が増えるんだろう?と思って見てみたら
HystrixMetricsStreamServletが追加されるようです。
https://github.com/wildfly-swarm/wildfly-swarm/blob/2017.1.1/fractions/netflix/hystrix/src/main/java/org/wildfly/swarm/netflix/hystrix/runtime/HystrixArchivePreparer.java
https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-metrics-event-stream
これは、HystrixのDashboardを使う時に必要になります。
呼び出し先の外部サービス
では、アプリケーションを書いていきましょう。
今回は、Spring Boot CLIで簡単なRestControllerを作ることにします。単純に「HelloWorld!!」と返すだけのRestControllerです。
hello-message.groovy
@RestController class HelloMessageController { @RequestMapping("hello-message") def message() { "HelloWorld!!" } }
このアプリケーションは、ポート9000でリッスンすることにしましょう。次のコマンドで起動します。
$ spring run hello-message.groovy -- --server.port=9000
以上で、呼び出し先の外部サービスの準備はおしまいです。
WildFly SwarmでHystrixを使う
続いて、WildFly Swarm+Hystrixを使ったアプリケーションを書きます。
Getting Startedの「Hello World!」を見ると、HystixCommandというクラスを継承して実装するようです。
Getting Started / Hello World!
そして、実際にどんな処理をするかは、runメソッドをオーバーライドすればよい、と。
Fail Fast
で、最初にこんなのを用意。
src/main/scala/org/littlewings/wildflyswarm/hystrix/FailFastMessageCommand.scala
package org.littlewings.wildflyswarm.hystrix import javax.ws.rs.client.ClientBuilder import com.netflix.hystrix.{HystrixCommand, HystrixCommandGroupKey} import org.jboss.logging.Logger class FailFastMessageCommand extends HystrixCommand[String](HystrixCommandGroupKey.Factory.asKey("FailFastMessageCommand")) { val logger: Logger = Logger.getLogger(classOf[FailFastMessageCommand]) override def run(): String = { logger.infof("start fail-fast request") val client = ClientBuilder .newClient try { val response = client .target("http://localhost:9000/hello-message") .request .get val message = response.readEntity(classOf[String]) response.close() logger.infof("end fail-fast request") message } finally { client.close() } } }
HystrixCommandのコンストラクタには、HystrixCommandGroupKeyを渡す必要があるようです。今回は、クラスの単純名から
キーを作成しました。
class FailFastMessageCommand extends HystrixCommand[String](HystrixCommandGroupKey.Factory.asKey("FailFastMessageCommand")) {
で、クラス名がなぜかFailFastになっていますが、これはドキュメントの次の箇所で、Fail Fastなパターンとして
紹介されているからです。
How To Use / Common Patterns / Fail Fast
この実装方法を取ると、runメソッドの呼び出しに失敗する(例外がスローされる)とHystrixRuntimeExceptionにその原因が
包まれてスローされるようです。それが、呼び出し元まで伝播してくる、と。
試してみましょう。こんなJAX-RSリソースクラスを用意。
src/main/scala/org/littlewings/wildflyswarm/hystrix/MessageResource.scala
package org.littlewings.wildflyswarm.hystrix import javax.ws.rs.core.MediaType import javax.ws.rs.{GET, Path, Produces} import org.jboss.logging.Logger @Path("message") class MessageResource { val logger: Logger = Logger.getLogger(classOf[MessageResource]) @GET @Path("fail-fast") @Produces(Array(MediaType.TEXT_PLAIN)) def failFast: String = { logger.infof("start resource") val command = new FailFastMessageCommand val message = command.execute() logger.infof("end resource") message + System.lineSeparator } }
ポイントは、オーバーライドしたrunメソッドではなくexecuteメソッドを呼び出すことですね。
val command = new FailFastMessageCommand val message = command.execute()
アプリケーションを起動します。
$ mvn wildfly-swarm:run
確認。
$ curl -i http://localhost:8080/message/fail-fast HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/plain Content-Length: 13 Date: Wed, 18 Jan 2017 11:39:12 GMT HelloWorld!!
では、続いてSpring Boot CLIで書いたアプリケーションを落としてアクセス。
$ curl -i http://localhost:8080/message/fail-fast HTTP/1.1 500 Internal Server Error Connection: keep-alive Content-Type: text/html;charset=UTF-8 Content-Length: 9359 Date: Wed, 18 Jan 2017 11:39:47 GMT <html><head><title>ERROR</title><style>body { font-family: "Lucida Grande", "Lucida Sans Unicode", "Trebuchet MS", Helvetica, Arial, Verdana, sans-serif; margin: 5px; } 〜省略〜
エラーになります。
サーバー(WildFly Swarmアプリケーション)裏では、HystrixRuntimeExceptionがスローされています。さらにその原因をたどると
接続エラーなわけですが。
Caused by: com.netflix.hystrix.exception.HystrixRuntimeException: FailFastMessageCommand failed and no fallback available.
落としたSpring Boot CLIアプリケーションを復帰させると、アクセス可能になります。
$ curl -i http://localhost:8080/message/fail-fast HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/plain Content-Length: 13 Date: Wed, 18 Jan 2017 11:41:45 GMT HelloWorld!!
Fail Silent
続いて、Fail Silentと紹介されているパターン。
How To Use / Common Patterns / Fail Silent
このケースで用意したクラス。
src/main/scala/org/littlewings/wildflyswarm/hystrix/FailSilentCommand.scala
package org.littlewings.wildflyswarm.hystrix import javax.ws.rs.client.ClientBuilder import com.netflix.hystrix.{HystrixCommand, HystrixCommandGroupKey} import org.jboss.logging.Logger class FailSilentCommand extends HystrixCommand[String](HystrixCommandGroupKey.Factory.asKey("FailSilentCommand")) { val logger: Logger = Logger.getLogger(classOf[FailSilentCommand]) override def run(): String = { logger.infof("start fail-silent request") val client = ClientBuilder .newClient try { val response = client .target("http://localhost:9000/hello-message") .request .get val message = response.readEntity(classOf[String]) response.close() logger.infof("end fail-silent request") message } finally { client.close() } } override def getFallback: String = "Fallback Message!!" }
Fail Fastで紹介したクラスとほとんど同じですが、今回はHystrixCommand#getFallbackメソッドをオーバーライドしています。
override def getFallback: String = "Fallback Message!!"
JAX-RSリソースクラスにもアクセス先を追加。
@Path("message") class MessageResource { val logger: Logger = Logger.getLogger(classOf[MessageResource]) @GET @Path("fail-fast") @Produces(Array(MediaType.TEXT_PLAIN)) def failFast: String = { logger.infof("start resource") val command = new FailFastMessageCommand val message = command.execute() logger.infof("end resource") message + System.lineSeparator } @GET @Path("fail-silent") @Produces(Array(MediaType.TEXT_PLAIN)) def failSilent: String = { logger.infof("start resource") val command = new FailSilentCommand val message = command.execute() logger.infof("end resource") message + System.lineSeparator }
WildFly Swarmに乗せたアプリケーションを動かして、確認。
$ curl -i http://localhost:8080/message/fail-silent HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/plain Content-Length: 13 Date: Wed, 18 Jan 2017 11:57:05 GMT HelloWorld!!
Spring Boot CLIで作ったアプリケーションを落として確認。
$ curl -i http://localhost:8080/message/fail-silent HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/plain Content-Length: 19 Date: Wed, 18 Jan 2017 11:57:40 GMT Fallback Message!!
オーバーライドしたgetFallbackメソッドで設定した値が返ってきました。
ちなみに、サーバー側では例外が飛んだりはしていません。
2017-01-18 20:57:40,472 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-9) start resource 2017-01-18 20:57:40,473 INFO [org.littlewings.wildflyswarm.hystrix.FailSilentCommand] (hystrix-FailSilentCommand-3) start fail-silent request 2017-01-18 20:57:40,492 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-9) end resource
というわけで、エラー発生時に代替の値を返す実装方法ですと。なので、「Silent」なわけですね。
HystrixCommandの呼び出し方法
少し、追加説明を。
今回、HystrixCommandのサブクラスを、すべてexecuteメソッドで呼び出しています。
val command = new FailFastMessageCommand val message = command.execute()
これは、「Synchronous Execution」、同期呼び出しになります。
How To Use / Synchronous Execution
他にはHystrixCommand#queueで呼び出す「Asynchronous Execution」、この場合はFutureが返ります。
How To Use / Asynchronous Execution
Observable(※RxJava)を返す「Reactive Execution」(HystrixCommand#observe、HystrixCommand#toObservable)。
How To Use / Reactive Execution
そしてHystrixObservableCommandを使っての「Reactive Commands」があります。
How To Use / Reactive Commands
呼び出しパターンとして覚えておきましょう。
設定を変更する
今回はデフォルトの設定でHystrixを動かしていますが、設定を行うこともできます。
ドキュメントはこちら。
Configuration · Netflix/Hystrix Wiki · GitHub
今回は、「circuitBreaker.requestVolumeThreshold」というプロパティを変えてみましょう。
Configuration / circuitBreaker.requestVolumeThreshold
実は、ここまでの動作確認では、Circuit Breakerがその名の通りの動作をしていることを確認していません。
「circuitBreaker.requestVolumeThreshold」というプロパティは、ある時間あたり、何回リクエストが失敗するとCircuitが
オープンするかを設定します。デフォルトは20で、オープンした後はCircuitがオープンした時間または最後にリクエスト呼び出しを
行った時間に、「circuitBreaker.sleepWindowInMilliseconds」で指定した時間が経過したのち、Circuitがクローズします。
デフォルトは5000(5秒)です。
今回、「circuitBreaker.requestVolumeThreshold」を5に設定したCommandを用意してみます。
src/main/scala/org/littlewings/wildflyswarm/hystrix/ConfiguredMessageCommand.scala
package org.littlewings.wildflyswarm.hystrix import javax.ws.rs.client.ClientBuilder import com.netflix.hystrix.{HystrixCommand, HystrixCommandGroupKey, HystrixCommandProperties} import org.jboss.logging.Logger class ConfiguredMessageCommand extends HystrixCommand[String]( HystrixCommand.Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey("ConfiguredMessageCommand")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withCircuitBreakerRequestVolumeThreshold(5)) ) { val logger: Logger = Logger.getLogger(classOf[ConfiguredMessageCommand]) override def run(): String = { logger.infof("start configured-message request") val client = ClientBuilder .newClient try { val response = client .target("http://localhost:9000/hello-message") .request .get val message = response.readEntity(classOf[String]) response.close() logger.infof("end configured-message request") message } finally { client.close() } } override def getFallback: String = "Fallback Message!!" }
ここまでのHystrixCommandの実装と異なり、HystrixCommand.SetterでGroupKeyを指定しつつ、プロパティを設定しています。
extends HystrixCommand[String]( HystrixCommand.Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey("ConfiguredMessageCommand")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withCircuitBreakerRequestVolumeThreshold(5)) ) {
なお、Fail Silentで実装しています。
「Confiugration」のドキュメントでいくと、「How to Set Instance Default」と書かれている方法ですね。というわけで、
このインスタンス単位の設定になります。
JAX-RSリソースクラス側に、このCommandの呼び出しを追加。
@GET @Path("configured") @Produces(Array(MediaType.TEXT_PLAIN)) def configured: String = { logger.infof("start configured-message resource") val command = new ConfiguredMessageCommand val message = command.execute() logger.infof("end configured-message resource") message + System.lineSeparator }
まずは、Spring Boot CLIアプリケーションが起動している状態でアクセス。
$ curl http://localhost:8080/message/configured
HelloWorld!!
この時、WildFly Swarm側で動かしているアプリケーションには、こんなログが出力されます。
2017-01-18 21:26:26,933 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-2) start configured-message resource 2017-01-18 21:26:26,935 INFO [org.littlewings.wildflyswarm.hystrix.ConfiguredMessageCommand] (hystrix-ConfiguredMessageCommand-2) start configured-message request 2017-01-18 21:26:26,995 INFO [org.littlewings.wildflyswarm.hystrix.ConfiguredMessageCommand] (hystrix-ConfiguredMessageCommand-2) end configured-message request 2017-01-18 21:26:26,997 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-2) end configured-message resource
JAX-RSリソースクラスのログと、間にHystrixCommandnのログが挟まっている感じですね。
では、Spring Boot CLIで作ったアプリケーションを落として、連続で6回アクセスしてみます。速すぎると切り替わらなかったので、
5回に1回で1秒スリープを…。
$ for i in {1..6}; do curl http://localhost:8080/message/configured; if [ $(( $i % 5 )) -eq 0 ]; then sleep 1; fi done Fallback Message!! Fallback Message!! Fallback Message!! Fallback Message!! Fallback Message!! Fallback Message!!
この時のログは、こんな感じ。
2017-01-18 21:41:34,953 INFO [org.littlewings.wildflyswarm.hystrix.ConfiguredMessageCommand] (hystrix-ConfiguredMessageCommand-10) start configured-message request 2017-01-18 21:41:34,961 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-12) end configured-message resource 2017-01-18 21:41:34,978 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-13) start configured-message resource 2017-01-18 21:41:34,979 INFO [org.littlewings.wildflyswarm.hystrix.ConfiguredMessageCommand] (hystrix-ConfiguredMessageCommand-10) start configured-message request 2017-01-18 21:41:34,985 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-13) end configured-message resource 2017-01-18 21:41:34,997 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-14) start configured-message resource 2017-01-18 21:41:34,998 INFO [org.littlewings.wildflyswarm.hystrix.ConfiguredMessageCommand] (hystrix-ConfiguredMessageCommand-10) start configured-message request 2017-01-18 21:41:35,002 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-14) end configured-message resource 2017-01-18 21:41:35,016 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-15) start configured-message resource 2017-01-18 21:41:35,016 INFO [org.littlewings.wildflyswarm.hystrix.ConfiguredMessageCommand] (hystrix-ConfiguredMessageCommand-10) start configured-message request 2017-01-18 21:41:35,025 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-15) end configured-message resource 2017-01-18 21:41:35,039 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-16) start configured-message resource 2017-01-18 21:41:35,039 INFO [org.littlewings.wildflyswarm.hystrix.ConfiguredMessageCommand] (hystrix-ConfiguredMessageCommand-10) start configured-message request 2017-01-18 21:41:35,045 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-16) end configured-message resource 2017-01-18 21:41:36,060 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-17) start configured-message resource 2017-01-18 21:41:36,061 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-17) end configured-message resource
6回目では、Command呼び出しそのものがなくなり、いきなりFallbackに移行しています。
※JAX-RSリソース側のログしかない
2017-01-18 21:41:36,060 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-17) start configured-message resource 2017-01-18 21:41:36,061 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-17) end configured-message resource
これがオープンした状態ですね、と。時間を置いてアクセスする(ウィンドウをまたぐと)と、再度Command呼び出しを再開します。
この例ではまだ呼び出しに失敗する状態のままですが。
2017-01-18 21:42:05,728 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-18) start configured-message resource 2017-01-18 21:42:05,728 INFO [org.littlewings.wildflyswarm.hystrix.ConfiguredMessageCommand] (hystrix-ConfiguredMessageCommand-10) start configured-message request 2017-01-18 21:42:05,733 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-18) end configured-message resource
先ほど実装した、単純なFail Silentの場合だと、デフォルト値なので20回まではCommandを呼び出し続けます。
$ for i in {1..21}; do curl http://localhost:8080/message/fail-silent; if [ $(( $i % 5 )) -eq 0 ]; then sleep 1; fi done
Commandの呼び出しがなくなるのは、21回目からですね。
2017-01-18 21:47:30,867 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-5) start resource 2017-01-18 21:47:30,868 INFO [org.littlewings.wildflyswarm.hystrix.FailSilentCommand] (hystrix-FailSilentCommand-10) start fail-silent request 2017-01-18 21:47:30,872 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-5) end resource 2017-01-18 21:47:31,903 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-6) start resource 2017-01-18 21:47:31,904 INFO [org.littlewings.wildflyswarm.hystrix.MessageResource] (default task-6) end resource
全体のデフォルト設定
今回、HystrixCommandのサブクラスの設定を個別に行いましたが、全体的にデフォルト値を設定することもできるようです。
設定例としては、こんな感じ。ConfigurationManagerというクラスを使えばよいみたいです。
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.circuitBreaker.requestVolumeThreshold", 5);
また、インスタンス単位で設定する場合は、こちら。
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.[HystrixCommandGroupKey名].circuitBreaker.requestVolumeThreshold", 5);
ただ、これをWildFly Swarmでやろうとすると、実行時に依存関係(archaius-core)が見えてないっぽいです。
Caused by: java.lang.NoClassDefFoundError: com/netflix/config/ConfigurationManager
HystrixFractionを見ると、使えるモジュールが「com.netflix.hystrix」と「io.reactivex.rxjava」に絞られている
みたいなので、そうかーという感じですが。
https://github.com/wildfly-swarm/wildfly-swarm/blob/2017.1.1/fractions/netflix/hystrix/src/main/java/org/wildfly/swarm/netflix/hystrix/HystrixFraction.java#L30-L33
HystrixFractionではHystrixMetricsStreamServletのパスしか設定できないみたいなので、全体的なデフォルト値は
触ることができないっぽいですねぇ。
Hystrix Dashboard
最後に、Hystrix Dashboardを使ってみます。最初の方で、Dashboardと連携するのに使う機能があるって書きましたしね。
Home · Netflix/Hystrix Wiki · GitHub
DashboardはWARとして提供されているので、こちらをダウンロードして使います。自分は、Maven Centralからダウンロードしました。
https://search.maven.org/#artifactdetails%7Ccom.netflix.hystrix%7Chystrix-dashboard%7C1.5.9%7Cwar
今回は、WildFly Swarmのmicroprofile-hollowswarm.jarを使います。
$ java -Dswarm.http.port=18080 -jar microprofile-2017.1.1-hollowswarm.jar hystrix-dashboard-1.5.9.war
リッスンポートは、18080としました。
この状態で、「http://localhost:18080/」にアクセスするとDashboardを見ることができます。
表示された赤枠のテキストフィールドに、今回作成したWildFly Swarmアプリケーションを起動した状態で
「http://localhost:8080/hystrix.stream」をテキストフィールドに埋め込み、「Add Stream」ボタンを押します。
すると、追加したURLが現れるので、この状態で「Monitor Stream」をクリックします。
Dashboardに移行するので、あとはHystrixMetricsStreamServletによって送信されたメトリクスを確認することが
できます。
先にも書きましたが、WildFly SwarmのHystrix向けのFractionを依存関係に追加すると、HystrixMetricsStreamServletが追加されます。
https://github.com/wildfly-swarm/wildfly-swarm/blob/2017.1.1/fractions/netflix/hystrix/src/main/java/org/wildfly/swarm/netflix/hystrix/runtime/HystrixArchivePreparer.java
https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-metrics-event-stream
これで、SSE(Server-Sent Events)を使ってクライアント側にメトリクスを送り続ける仕組みになっているみたいです。
※Server-Sent Eventsよく知らないですが
Metrics and Monitoring / Metrics Event Stream
オマケ - HystrixConcurrencyStrategy
WildFly Swarmのチュートリアルでは、なにやら起動時にThreadFactoryの設定をしているコードがあります。
https://github.com/wildfly-swarm/tutorial/blob/master/microservice/everest/src/main/java/org/javaee7/wildfly/samples/everest/HystrixEEBootstrap.java
https://github.com/wildfly-swarm/tutorial/blob/master/microservice/everest/src/main/java/org/javaee7/wildfly/samples/everest/EEConcurrencyStrategy.java
このコードでは、@ResourceでインジェクションしたManagedThreadFactoryを使ってThreadPoolを作成し、HystrixPluginsに
HystrixConcurrencyStrategyとして登録します。
@Resource ManagedThreadFactory threadFactory; @PostConstruct public void onStartup() { System.out.println("Initialising hystrix ..."); HystrixPlugins.getInstance().registerConcurrencyStrategy(new EEConcurrencyStrategy(threadFactory)); }
これは、Hystrixのプラグインの仕組みのひとつですね。
Plugins · Netflix/Hystrix Wiki · GitHub
Plugins / Concurrency Strategy
このチュートリアルのコードでは、Hystrixが使用するThreadPoolで使うThreadを、ManagedThreadFactoryを使って
作成するように変更しています。まあ、EE環境だからってことなのでしょうね。
まとめ
WildFly Swarm(自体はそこまで関係なかったですけど)を使って、HystrixのFractionを組み込み、基本的な使い方とDashboardの連携まで
見てみました。
Circuit Breakerを使ったのは初めてだったのですが、なかなか面白かったです。
今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/wildfly-swarm-scala-examples/tree/master/circuit-breaker-hystrix
参考)
マイクロサービスにレジリエンスをもたらすHystrixを試してみる - たけぞう瀕死ブログ
Spring BootでCircuit Breaker(Spring Cloud Netflix Hystrix)を試す - abcdefg.....