title: 分散フレームワークakkaの調査 author: Takahiro Shimizu profile: lang: Japanese # 調査目的 * 先輩の修論の比較材料の為に行う * 分散フレームワークの1つであるakkaがどのような書き方、及び処理なのかを調査する # 調査内容 * akkaのmac osx,ubuntu上の導入 * [公式チュートリアル](https://developer.lightbend.com/guides/akka-quickstart-java/)の例題を行う * 今回はJavaで実行した # 環境構築 * 公式からzipファイルで提供されているので落とす * 例題はGradleで実行できるように作成されているので `$ gradle run` を実行可能. # akkaの分散処理 * akkaはアクターモデルを採用したフレームワーク * アクターモデルでは「アクター」と呼ばれるオブジェクト同士がメッセージ通信を行うことで並列処理を実現する(イベント・ドリブン) * publicなAPIを持っていない為,強力な分離機能を持っており、複数のJVMなどでも連携可能 * 環境透過性 * 軽量 * JVM上で動き、ScalaとJavaをサポートしている # 例題(Hello,World) * 今回は公式チュートリアルにある、複数のアクターが挨拶をするHello Worldの例題を実行する * * 実行結果 ``` $ gradle run Starting a Gradle Daemon (subsequent builds will be faster) > Task :run >>> Press ENTER to exit <<< [INFO] [01/16/2018 15:33:05.871] [helloakka-akka.actor.default-dispatcher-4] [akka://helloakka/user/printerActor] Howdy, Akka [INFO] [01/16/2018 15:33:05.872] [helloakka-akka.actor.default-dispatcher-4] [akka://helloakka/user/printerActor] Howdy, Lightbend [INFO] [01/16/2018 15:33:05.872] [helloakka-akka.actor.default-dispatcher-4] [akka://helloakka/user/printerActor] Good day, Play [INFO] [01/16/2018 15:33:05.872] [helloakka-akka.actor.default-dispatcher-4] [akka://helloakka/user/printerActor] Hello, Java ``` # Hello World Actors * 例題のActorrは3種類のmessageを利用する * `WhoToGreet` * greetingの受取用オブジェクト * `Greet` * greetingの実行用 * `Greeting` * `greeting`にメッセージを含める為の命令 * 複数のスレッドで共有をする必要がある為、メッセージはimmutableでなければならない # Greeter Actor * `Greeter` のコンストラクタは、送信用メッセージと出力用のActorのリファレンスを必要とする * mainの中ではGreeterは複数呼ばれている ``` package com.lightbend.akka.sample; import akka.actor.AbstractActor; import akka.actor.ActorRef; import akka.actor.Props; import com.lightbend.akka.sample.Printer.Greeting; public class Greeter extends AbstractActor { static public Props props(String message, ActorRef printerActor) { return Props.create(Greeter.class, () -> new Greeter(message, printerActor)); } static public class WhoToGreet { public final String who; public WhoToGreet(String who) { this.who = who; } } static public class Greet { public Greet() { } } private final String message; private final ActorRef printerActor; private String greeting = ""; public Greeter(String message, ActorRef printerActor) { this.message = message; this.printerActor = printerActor; } @Override public Receive createReceive() { return receiveBuilder() .match(WhoToGreet.class, wtg -> { this.greeting = message + ", " + wtg.who; }) .match(Greet.class, x -> { printerActor.tell(new Greeting(greeting), getSelf()); }) .build(); } } ``` # Printer Actor * `Logging.getLogger(getContext().getSystem(), this);` で各Actorが `log.info()` に追記していく * `Greeting` とlogsに対してhandleを所持している ``` package com.lightbend.akka.sample; import akka.actor.AbstractActor; import akka.actor.ActorRef; import akka.actor.Props; import akka.event.Logging; import akka.event.LoggingAdapter; public class Printer extends AbstractActor { static public Props props() { return Props.create(Printer.class, () -> new Printer()); } static public class Greeting { public final String message; public Greeting(String message) { this.message = message; } } private LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this); public Printer() { } @Override public Receive createReceive() { return receiveBuilder() .match(Greeting.class, greeting -> { log.info(greeting.message); }) .build(); } } ``` # Actorを作る * akkaはインスタンスを作る際に `new` を使わない。これはakkaのインスタンスがリファレンスである為 (`akka.actor.ActorRef`)である。 * その為、軽量かつ柔軟にシステムに組み込むことが可能である * akkaのActorは `akka.actor.ActorSystem` が管理する。(factoryなどとも呼ばれる) * 例題では次のようにakkaのインスタンスを作成している。 ``` public class AkkaQuickstart { public static void main(String[] args) { final ActorSystem system = ActorSystem.create("helloakka"); try { //#create-actors final ActorRef printerActor = system.actorOf(Printer.props(), "printerActor"); final ActorRef howdyGreeter = system.actorOf(Greeter.props("Howdy", printerActor), "howdyGreeter"); final ActorRef helloGreeter = system.actorOf(Greeter.props("Hello", printerActor), "helloGreeter"); final ActorRef goodDayGreeter = system.actorOf(Greeter.props("Good day", printerActor), "goodDayGreeter"); //#create-actors ``` # メッセージ送信 * akkaのメッセージ送信は `ActorRef`の`tell`メソッドを呼ぶ。 ``` howdyGreeter.tell(new WhoToGreet("Akka"), ActorRef.noSender()); howdyGreeter.tell(new Greet(), ActorRef.noSender()); howdyGreeter.tell(new WhoToGreet("Lightbend"), ActorRef.noSender()); howdyGreeter.tell(new Greet(), ActorRef.noSender()); helloGreeter.tell(new WhoToGreet("Java"), ActorRef.noSender()); helloGreeter.tell(new Greet(), ActorRef.noSender()); goodDayGreeter.tell(new WhoToGreet("Play"), ActorRef.noSender()); goodDayGreeter.tell(new Greet(), ActorRef.noSender()); ``` * `Greeter` Actrorは `Printer` Actorにメッセージを送信している ``` printerActor.tell(new Greeting(greeting), getSelf()); ``` # テスト * Javaで使われているのでテストはJUnitを利用できる * akkaaでは `akka.test.javadsl.TestKit` が用意されており, TestKit が推奨されている。 * 詳しくは[公式ドキュメント](https://doc.akka.io/docs/akka/current/testing.html?language=java)を見ろということらしい… ``` `ckage com.lightbend.akka.sample; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.testkit.javadsl.TestKit; import com.lightbend.akka.sample.Greeter.*; import com.lightbend.akka.sample.Printer.*; import static org.junit.Assert.assertEquals; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class AkkaQuickstartTest { static ActorSystem system; @BeforeClass public static void setup() { system = ActorSystem.create(); } @AfterClass public static void teardown() { TestKit.shutdownActorSystem(system); system = null; } @Test public void testGreeterActorSendingOfGreeting() { final TestKit testProbe = new TestKit(system); final ActorRef helloGreeter = system.actorOf(Greeter.props("Hello", testProbe.getRef())); helloGreeter.tell(new WhoToGreet("Akka"), ActorRef.noSender()); helloGreeter.tell(new Greet(), ActorRef.noSender()); Greeting greeting = testProbe.expectMsgClass(Greeting.class); assertEquals("Hello, Akka", greeting.message); } } ```