view slides/2018/20180116/slide.md @ 23:c0ec001d8a28

update
author Takahiro SHIMIZU <anatofuz@cr.ie.u-ryukyu.ac.jp>
date Thu, 05 Apr 2018 11:34:39 +0900
parents slides/20180116/slide.md@deecd8254ba8
children
line wrap: on
line source

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の例題を実行する
* <img src="https://developer.lightbend.com/guides/akka-quickstart-java/images/hello-akka-architecture.png">

* 実行結果

```
$ 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);
    }
}
```