# Scala+Akka 実践ティップス

**URL:** https://forum.ficusonline.com/t/topic/394
**Category:** Programming
**Created:** [2020 年 11 月 12 日午前 9:03 UTC](https://forum.ficusonline.com/t/topic/394 "2020-11-12T09:03:33Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![tk-fuse](https://forum.ficusonline.com/user_avatar/forum.ficusonline.com/tk-fuse/32/255_2.png) [@tk-fuse](https://forum.ficusonline.com/u/tk-fuse)
#### Post date: [2020 年 11 月 12 日午前 9:03 UTC](https://forum.ficusonline.com/t/topic/394/1 "2020-11-12T09:03:33Z")

</div>

## 実践的なコードによるScalaティップス

当初、 **Scala** の導入目的は、オープンソースアプリのバグ修正・コード改訂でしたが、アクターモデルによるアルゴリズムがデータマネージメントアプリを作成するのに有用だと考え、より深く学習することにしました。導入時のややっこしい内容を、実際のコードで理解するためのティップスをここに纏めます。サンプルコード内でのActorは、旧版（Classic）のAPIにより記述されていることが多いため、時間があれば新APIに変換してみて下さい。新旧API混在での記述もサポートしています。

## パターンマッチング

[https://dzone.com/articles/8-scala-pattern-matching-tricks](https://dzone.com/articles/8-scala-pattern-matching-tricks)

### Name Binding

```auto
def requestMoreInfo(p: Person): String = { ... }
val bob = Person("Bob", 34, List("Inception", "The Departed"))
val bobsInfo = bob match {
    case Person(name, age, movies) => s"$name's info: ${requestMoreInfo(Person(name, age, movies))}"
}

```

**“p @”** による代替

```auto
val bobsInfo = bob match {
    case p @ Person(name, _, _) => s"$name's info: ${requestMoreInfo(p)}"
}

```

**“movies @”** による代替

```auto
val bobsInception = bob match {
    case Person(name, _, movies @ List("Inception", _*)) => s"$name REALLY likes Inception, some other movies too: $movies"
}

```

---

<div class="post-metadata">

### Author: ![tk-fuse](https://forum.ficusonline.com/user_avatar/forum.ficusonline.com/tk-fuse/32/255_2.png) [@tk-fuse](https://forum.ficusonline.com/u/tk-fuse)
#### Post date: [2020 年 11 月 30 日午前 9:43 UTC](https://forum.ficusonline.com/t/topic/394/2 "2020-11-30T09:43:33Z")

</div>

## Akkaアクターシステムの設定とSLF4Jによるアクターのログ取得

> **[Logging • Akka core](https://doc.akka.io/libraries/akka-core/current/typed/logging.html)**
>
> Logging options with Akka.

アクター間のメッセージの遣り取りを、SLF4Jによるログシステムにより確認します。スケジューラーにより5秒毎にメッセージの遣り取りが実行されます。

> **[akka-in-action/chapter-conf-deploy at master · capitalfuse/akka-in-action](https://github.com/capitalfuse/akka-in-action/tree/master/chapter-conf-deploy)**
>
> Accompanying source code for akka in action . Contribute to capitalfuse/akka-in-action development by creating an account on GitHub.

 ![chapter-conf-deploy](https://forum.ficusonline.com/uploads/default/original/1X/fa2f8ccc5f4cad44034a6dc1e120459b87e22f8b.png)

**build.sbt**

```auto
name := "deploy"

version := "1.0"

organization := "manning"

scalacOptions ++= Seq(
  "-deprecation",
  "-unchecked",
  "-Xlint",
  "-Ywarn-unused",
  "-Ywarn-dead-code",
  "-feature",
  "-language:_"
)

enablePlugins(JavaAppPackaging)

scriptClasspath +="../conf"

libraryDependencies ++= {
  val akkaVersion = "2.6.10"
  Seq(
    "com.typesafe.akka" %% "akka-actor" % akkaVersion,
    "com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
    "ch.qos.logback" % "logback-classic" % "1.2.3",
    "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
    "org.scalatest" %% "scalatest" % "3.2.2" % "test"
  )
}

```

**scala.sbt**

```auto
scalaVersion := "2.13.1"

scalacOptions ++= Seq(
  "-deprecation",
  "-unchecked",
  "-Xfatal-warnings",
  "-feature",
  "-language:_"
)

```

**appilication.conf**

> **[Akka 2.10.17 - akka.event.slf4j.Slf4jLogger](https://doc.akka.io/api/akka-core/current//akka/event/slf4j/Slf4jLogger.html)**
>
> Akka 2.10.17 - akka.event.slf4j.Slf4jLogger

```auto
akka {
  loggers = ["akka.event.slf4j.Slf4jLogger"]

  # Options: ERROR, WARNING, INFO, DEBUG
  loglevel = "DEBUG"
}

```

**reference.conf**

```auto
helloWorld {
    timer=5000
}

```

**BootHello.scala**

```auto
package aia.deploy

import akka.actor.{ Props, ActorSystem }
import scala.concurrent.duration._

object BootHello extends App {

  val system = ActorSystem("hello-kernel")

  val actor = system.actorOf(Props[HelloWorld])
  val config = system.settings.config
  val timer = config.getInt("helloWorld.timer")
  system.actorOf(Props(
      new HelloWorldCaller(
        timer = timer millis,
        actor = actor)))
}

```

**HelloWorld.scala**

```auto
package aia.deploy

import akka.actor.{Actor, ActorLogging, ActorRef}

import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration._

class HelloWorld extends Actor
  with ActorLogging {

  def receive: Receive = {
    case msg: String =>
      val hello = "Hello %s".format(msg)
      sender() ! hello
      log.info("Sent response {}",hello)
  }
}

class HelloWorldCaller(timer: FiniteDuration, actor: ActorRef)
  extends Actor with ActorLogging {

  case class TimerTick(msg: String)

  override def preStart(): Unit = {
    super.preStart()
    implicit val ec: ExecutionContextExecutor = context.dispatcher
    context.system.scheduler.scheduleAtFixedRate(
      timer,
      timer,
      self,
      TimerTick("everybody"))
  }

  def receive: Receive = {
    case msg: String => log.info("received {}",msg)
    case tick: TimerTick => actor ! tick.msg
  }
}

```

**実行結果**

```auto
[INFO] [11/30/2020 17:47:30.471] [hello-kernel-akka.actor.default-dispatcher-6] [akka://hello-kernel/user/$a] Sent response Hello everybody
[INFO] [11/30/2020 17:47:30.471] [hello-kernel-akka.actor.default-dispatcher-7] [akka://hello-kernel/user/$b] received Hello everybody
[INFO] [11/30/2020 17:47:35.439] [hello-kernel-akka.actor.default-dispatcher-5] [akka://hello-kernel/user/$a] Sent response Hello everybody
[INFO] [11/30/2020 17:47:35.439] [hello-kernel-akka.actor.default-dispatcher-7] [akka://hello-kernel/user/$b] received Hello everybody
[INFO] [11/30/2020 17:47:40.439] [hello-kernel-akka.actor.default-dispatcher-5] [akka://hello-kernel/user/$a] Sent response Hello everybody
[INFO] [11/30/2020 17:47:40.439] [hello-kernel-akka.actor.default-dispatcher-7] [akka://hello-kernel/user/$b] received Hello everybody
[INFO] [11/30/2020 17:47:45.438] [hello-kernel-akka.actor.default-dispatcher-7] [akka://hello-kernel/user/$a] Sent response Hello everybody
[INFO] [11/30/2020 17:47:45.438] [hello-kernel-akka.actor.default-dispatcher-5] [akka://hello-kernel/user/$b] received Hello everybody
........................
........................

```

---

<div class="post-metadata">

### Author: ![tk-fuse](https://forum.ficusonline.com/user_avatar/forum.ficusonline.com/tk-fuse/32/255_2.png) [@tk-fuse](https://forum.ficusonline.com/u/tk-fuse)
#### Post date: [2020 年 12 月 3 日午前 5:23 UTC](https://forum.ficusonline.com/t/topic/394/3 "2020-12-03T05:23:13Z")

</div>

### REST API サンプルコード

> **[Chapter 2. Up and running · Akka in Action](https://livebook.manning.com/book/akka-in-action/chapter-2/33)**
>
> Fetching a project template · Building a minimal Akka app for the cloud · Deploying to Heroku

**イベントとそのチケット数の登録・チケット購入システム**

> **[akka-in-action/chapter-up-and-running at master · capitalfuse/akka-in-action](https://github.com/capitalfuse/akka-in-action/tree/master/chapter-up-and-running)**
>
> Accompanying source code for akka in action . Contribute to capitalfuse/akka-in-action development by creating an account on GitHub.

 ![rest-api](https://forum.ficusonline.com/uploads/default/original/1X/96442a89fce8edc83f88dff77f16bf774a6e0a1d.png)

 ![actor-creation-sequence](https://forum.ficusonline.com/uploads/default/original/1X/f3f4b8a14343bfce39a7fe7b40ea9f06beb19452.png)

 ![creating-event](https://forum.ficusonline.com/uploads/default/original/1X/3bbb15e497fa9d907fa68d43bafa8f8696b2c79c.png)

 ![buying-ticket](https://forum.ficusonline.com/uploads/default/original/1X/e68e06b380652e237dba16c75e923df6e602a3b8.png)

プロジェクトフォルダでサーバ起動

```auto
$ sbt run
INFO [Slf4jLogger]: Slf4jLogger started
Success
INFO [go-ticks]: RestApi bound to /0:0:0:0:0:0:0:0:5000 

```

イベントRHCPのチケットを10枚ポスト

```auto
$ http POST localhost:5000/events/RHCP tickets:=10
HTTP/1.1 201 Created
Content-Length: 28
Content-Type: application/json
Date: Thu, 03 Dec 2020 04:45:27 GMT
Server: GoTicks.com REST API

{
    "name": "RHCP",
    "tickets": 10
}

```

イベントDjMadlibのチケットを15枚ポスト

```auto
$ http POST localhost:5000/events/DjMadlib tickets:=15
HTTP/1.1 201 Created
Content-Length: 32
Content-Type: application/json
Date: Thu, 03 Dec 2020 04:45:51 GMT
Server: GoTicks.com REST API

{
    "name": "DjMadlib",
    "tickets": 15
}

```

イベント詳細確認

```auto
$ http GET localhost:5000/events
HTTP/1.1 200 OK
Content-Length: 74
Content-Type: application/json
Date: Thu, 03 Dec 2020 04:46:42 GMT
Server: GoTicks.com REST API

{
    "events": [
        {
            "name": "DjMadlib",
            "tickets": 15
        },
        {
            "name": "RHCP",
            "tickets": 10
        }
    ]
}

```

イベントRHCPのチケットを2枚購入

```auto
$ http POST localhost:5000/events/RHCP/tickets tickets:=2
HTTP/1.1 201 Created
Content-Length: 46
Content-Type: application/json
Date: Thu, 03 Dec 2020 04:46:59 GMT
Server: GoTicks.com REST API

{
    "entries": [
        {
            "id": 1
        },
        {
            "id": 2
        }
    ],
    "event": "RHCP"
}

```

イベント詳細確認（RHCPのチケット数確認）

```auto
$ http GET localhost:5000/events
HTTP/1.1 200 OK
Content-Length: 73
Content-Type: application/json
Date: Thu, 03 Dec 2020 04:47:35 GMT
Server: GoTicks.com REST API

{
    "events": [
        {
            "name": "DjMadlib",
            "tickets": 15
        },
        {
            "name": "RHCP",
            "tickets": 8
        }
    ]
}

```

---

<div class="post-metadata">

### Author: ![tk-fuse](https://forum.ficusonline.com/user_avatar/forum.ficusonline.com/tk-fuse/32/255_2.png) [@tk-fuse](https://forum.ficusonline.com/u/tk-fuse)
#### Post date: [2020 年 12 月 4 日午前 6:21 UTC](https://forum.ficusonline.com/t/topic/394/4 "2020-12-04T06:21:26Z")

</div>

### “onComplete” to handle success and failure

**Scalaシェル(REPL)**

```auto
scala> :paste
// Entering paste mode (ctrl-D to finish)

import scala.util._ // Imports statement for Try, Success, and Failure
import scala.concurrent._
import ExecutionContext.Implicits.global

val futureFail = Future { throw new Exception("error!")}
futureFail.onComplete {
case Success(value) => println(value)
case Failure(e) => println(e)
}

// Exiting paste mode, now interpreting.

```

**Exceptionによるエラー警告表示**

```auto
java.lang.Exception: error!

```

---

<div class="post-metadata">

### Author: ![tk-fuse](https://forum.ficusonline.com/user_avatar/forum.ficusonline.com/tk-fuse/32/255_2.png) [@tk-fuse](https://forum.ficusonline.com/u/tk-fuse)
#### Post date: [2020 年 12 月 7 日午前 8:57 UTC](https://forum.ficusonline.com/t/topic/394/5 "2020-12-07T08:57:43Z")

</div>

### REST APIサンプルコードの分割処理

> **[Chapter 6. Your first distributed Akka app · Akka in Action](https://livebook.manning.com/book/akka-in-action/chapter-6/17)**
>
> Introducing scaling out · Distributing the GoTicks.com app · Distributing actors with the remote module · Testing distributed actor systems

負荷分散・拡張性を目的とした **リモートノード** を **バックエンド** として配置

> **[akka-in-action/chapter-remoting at master · capitalfuse/akka-in-action](https://github.com/capitalfuse/akka-in-action/tree/master/chapter-remoting)**
>
> Accompanying source code for akka in action . Contribute to capitalfuse/akka-in-action development by creating an account on GitHub.

シングルノードによる処理からノード分割による処理

 ![remote-lookup](https://forum.ficusonline.com/uploads/default/original/1X/766957c69445816785ba1565bb2dd691874b17d4.png)

 ![RemoteLookupProxy-actor](https://forum.ficusonline.com/uploads/default/original/1X/c28bf45dee03dfab09c25fa01969ffc31163cf5c.png)

**IntelliJ** の **sbtシェル** か **Runメニュ** ーから  
**FrontendRemoteDeployMain**  
を実行

```auto
[IJ]sbt:goticks> run
[warn] Multiple main classes detected. Run 'show discoveredMainClasses' to see the list

Multiple main classes detected, select one to run:

 [1] com.goticks.BackendMain
 [2] com.goticks.BackendRemoteDeployMain
 [3] com.goticks.FrontendMain
 [4] com.goticks.FrontendRemoteDeployMain
 [5] com.goticks.FrontendRemoteDeployWatchMain
 [6] com.goticks.SingleNodeMain

Enter number: 4

[info] running com.goticks.FrontendRemoteDeployMain 
[DEBUG] [12/07/2020 23:26:00.983] [run-main-0] [EventStream] StandardOutLogger started
INFO [Slf4jLogger]: Slf4jLogger started
[DEBUG] [12/07/2020 23:26:01.872] [run-main-0] [EventStream(akka://frontend)] logger log1-Slf4jLogger started
[DEBUG] [12/07/2020 23:26:01.874] [run-main-0] [EventStream(akka://frontend)] Default Loggers started
WARN [RemoteActorRefProvider]: Using the 'remote' ActorRefProvider directly, which is a low-level layer. For most use cases, the 'cluster' abstraction on top of remoting is more suitable instead.
WARN [RemoteActorRefProvider]: Akka Cluster not in use - Using Akka Cluster is recommended if you need remote watch and deploy.
INFO [ArteryTcpTransport]: Remoting started with transport [Artery tcp]; listening on address [akka://frontend@127.0.1.1:25520] with UID [8779393747958428879]
Success
INFO [go-ticks]: RestApi bound to /0:0:0:0:0:0:0:0:5000 

```

フロントエンドとバックエンドが起動するので、同じくIntelliJのターミナルからhttpユーティリティにより、イベントとそのチケット数の **POST** や、 **GET** にてイベントの確認などをして下さい。

```auto
$ http POST localhost:5000/events/RHCP tickets:=10
HTTP/1.1 201 Created
Content-Length: 28
Content-Type: application/json
Date: Mon, 07 Dec 2020 14:32:23 GMT
Server: GoTicks.com REST API

{
    "name": "RHCP",
    "tickets": 10
}

```

```auto
$ http GET localhost:5000/events/RHCP
HTTP/1.1 200 OK
Content-Length: 28
Content-Type: application/json
Date: Mon, 07 Dec 2020 14:33:38 GMT
Server: GoTicks.com REST API

{
    "name": "RHCP",
    "tickets": 10
}

```

---

<div class="post-metadata">

### Author: ![tk-fuse](https://forum.ficusonline.com/user_avatar/forum.ficusonline.com/tk-fuse/32/255_2.png) [@tk-fuse](https://forum.ficusonline.com/u/tk-fuse)
#### Post date: [2020 年 12 月 8 日午前 5:27 UTC](https://forum.ficusonline.com/t/topic/394/6 "2020-12-08T05:27:59Z")

</div>

### Akkaクラスター

**Multi JVM Testing**

> **[Multi JVM Testing • Akka core](https://doc.akka.io/libraries/akka-core/current/multi-jvm-testing.html)**
>
> Multi JVM testing of distributed systems built with Akka.

**Multi Node Testing**

> **[Multi Node Testing • Akka core](https://doc.akka.io/libraries/akka-core/current/multi-node-testing.html)**
>
> Multi node testing of distributed systems built with Akka.

**Akkaクラスタードキュメント**

> **[Cluster Usage • Akka core](https://doc.akka.io/libraries/akka-core/current/typed/cluster.html)**
>
> Build distributed applications that scale across the network with Akka Cluster, a fault-tolerant decentralized peer-to-peer based cluster node membership service with no single point of failure.

> **[Chapter 14. Clustering · Akka in Action](https://livebook.manning.com/book/akka-in-action/chapter-14/)**
>
> Dynamically scaling actors in a cluster · Sending messages into the cluster with cluster-aware routers · Building a clustered Akka app

 ![akka-cluster-jobprocess](https://forum.ficusonline.com/uploads/default/original/1X/e6d74ec4f177039bb2fcad4a25d262b8c8e5713e.jpeg)

 ![akka-cluster-process](https://forum.ficusonline.com/uploads/default/original/1X/cf6dfdf55e474bbd88c2d22e5b23de12c82e0175.jpeg)

**サンプルコード開発環境変更**

- Java SDK 11.0.9
- Scala:2.13.4
- Akka:2.6.10
- sbt:1.3.13

旧リモート設定(TCP)は非推奨のため、 **Artery** リモート設定(UDP)へ変更

**Classic Remoting**

> **[Remoting • Akka core](https://doc.akka.io/libraries/akka-core/current/remoting-artery.html)**
>
> Details about the underlying remoting module for Akka Cluster.

**Artery Remoting**

> **[Remoting • Akka core](https://doc.akka.io/libraries/akka-core/current/remoting-artery.html)**
>
> Details about the underlying remoting module for Akka Cluster.

**移行ガイド**

> **[Migration Guide 2.5.x to 2.6.x • Akka core](https://doc.akka.io/libraries/akka-core/current/project/migration-guide-2.5.x-2.6.x.html)**
>
> Migrating to Akka 2.6.

**Split Brain Resolver**

> **[Split Brain Resolver • Akka core](https://doc.akka.io/libraries/akka-core/current/split-brain-resolver.html)**
>
> Akka is a toolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala.

**Downing**

> **[Cluster Usage • Akka core](https://doc.akka.io/libraries/akka-core/current/typed/cluster.html)**
>
> Build distributed applications that scale across the network with Akka Cluster, a fault-tolerant decentralized peer-to-peer based cluster node membership service with no single point of failure.

**Akkaクラスターサンプルコード**

> **[akka-in-action/chapter-cluster at master · capitalfuse/akka-in-action](https://github.com/capitalfuse/akka-in-action/tree/master/chapter-cluster)**
>
> Accompanying source code for akka in action . Contribute to capitalfuse/akka-in-action development by creating an account on GitHub.

メッセージとして以下の **List** を送信し、 **Worker** が各単語の合計数をカウントする。

`List("this is a test ", "this is a test", "this is", "this")`

タスクフロー

 ![deploy-job-workers](https://forum.ficusonline.com/uploads/default/original/1X/a4922f5f2d9ccbd6938453ed32d53fbddcc83882.jpeg)

 ![jobworker-enlist](https://forum.ficusonline.com/uploads/default/original/1X/ee2e10056cca1e7680ed7d666a3cddcf4d3b5ba1.jpeg)

 ![jobworker-send-task](https://forum.ficusonline.com/uploads/default/original/1X/1e5a960b49626bd581af3815d5291a442e223468.jpeg)

 ![return-task-result](https://forum.ficusonline.com/uploads/default/original/1X/d202df7628256f6acc2d0aaf48e5c8225a303ca2.jpeg)

 ![terminate-worker](https://forum.ficusonline.com/uploads/default/original/1X/a50a43d8e11b765f0f2629ea070a813d379c4a32.jpeg)

**変更箇所**  
最新のSDKで動作確認できるようコード修正、ファイル追加など

> <https://github.com/capitalfuse/akka-in-action/commit/845caec13a990fa8ebc32f8b35c5fdbc069dd33e>

**実行** （test2は1回のみカウント、test3はListを重複させ100回カウント、test4は”FAIL”検出によるエラー処理テスト）

```auto
$ sbt test
[info] welcome to sbt 1.3.13 (AdoptOpenJDK Java 11.0.9.1)
[info] loading global plugins from /home/user/.sbt/1.0/plugins
[info] loading settings for project chapter-cluster-build from plugins.sbt ...
[info] loading project definition from /home/user/IdeaProjects/akka-in-action/chapter-cluster/project
[info] loading settings for project chapter-cluster from scala.sbt,build.sbt ...
[info] set current project to words-cluster (in build file:/home/user/IdeaProjects/akka-in-action/chapter-cluster/)
INFO [Slf4jLogger]: Slf4jLogger started
INFO [TestReceptionist]: Received job test2
...
...

INFO [TestReceptionist]: Job test2 complete.
INFO [TestReceptionist]: result:Map(this -> 4, is -> 3, a -> 2, test -> 2)

...
...
INFO [TestReceptionist]: Job test3 complete.
INFO [TestReceptionist]: result:Map(this -> 800, is -> 600, a -> 400, test -> 400)
INFO [TestReceptionist]: Received job test4

...
ERROR [TestReceptionist]: Job Master Actor[akka://test/user/receptionist/master-test4#-490024209] terminated before finishing job.
ERROR [TestReceptionist]: Job test4 failed.

...
INFO [TestReceptionist]: Job test4 complete.
INFO [TestReceptionist]: result:Map(this -> 1, is -> 1, a -> 1, test -> 1)

...
[info] LocalWordsSpec:
[info] The words system
[info] - must count the occurrence of words in a text
[info] - must count many occurences of words in a text
[info] - must continue to process a job with intermittent failures
[info] Run completed in 3 seconds, 285 milliseconds.
[info] Total number of tests run: 3
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 3, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 8 s, completed Dec 12, 2020, 10:30:53 PM

```

設定ファイルによる各ノード（シードノード、マスターノード、ワーカーノード）の動作確認

**sbt-assembly** により **jar** ファイル作成( **target/words-node.jar** )

```auto
$ sbt assemply

```

ポートと設定ファイルを指定してノード起動（別ターミナルで起動すること）

> $ java -DPORT=2551 -Dconfig.resource=/seed.conf -jar target/words-node.jar

> $ java -DPORT=2554 -Dconfig.resource=/master.conf -jar target/words-node.jar

> $ java -DPORT=2555 -Dconfig.resource=/worker.conf -jar target/words-node.jar

> $ java -DPORT=2556 -Dconfig.resource=/worker.conf -jar target/words-node.jar
