1. 概要

ライブラリAkkaTyped の紹介の後、アクター間の主要な相互作用パターンの1つであるTell Pattern(ファイアアンドフォーゲットとも呼ばれます)について詳しく説明します。

2. Akkaの依存関係

Akka Typedライブラリを使用するには、テスト用にakka-actor-typedおよびakka-actor-testkit-typedをインポートする必要があります。

libraryDependencies += "com.typesafe.akka" % "akka-actor-typed_2.12" % "2.6.8",
libraryDependencies += "com.typesafe.akka" % "akka-actor-testkit-typed_2.12" % "2.6.8" % Test,

3. シナリオ

アクターの相互作用について話すには、最初にいくつかのアクターを定義する必要があります。 Akkaライブラリとアクターモデルを使用してマイクロサービスアーキテクチャを開発していると想像してください。 マイクロサービスアーキテクチャの原則の1つは、監視可能なサービスを作成することです

監視可能なサービスは、ログ集約パターンを実装する必要があります。 したがって、サービスは、検索とアラートを提供する一元化されたロギングサーバーにログを書き込む必要があります。

Akkaアクターを使用してパターンを実装したいと思います。 まず、目的のプロトコルを定義する必要があります。 簡単にするために、ログには3つのログレベルしかありません。

sealed trait Log
final case class Trace(msg: String) extends Log
final case class Info(msg: String) extends Log
final case class Error(msg: String) extends Log

ロギングメッセージを受信したアクターは、Akkaライブラリの標準ロギングシステムを使用してメッセージをログに記録するだけです。

object LogKeeperActor {
  def apply(): Behavior[Log] = {
    Behaviors.receive { (context, message) =>
      message match {
        case Trace(msg) => context.log.trace(msg)
        case Info(msg) => context.log.info(msg)
        case Error(msg) => context.log.error(msg)
      }
      Behaviors.same
    }
  }
}

欠落している唯一のステップは、ログメッセージをLogKeeperActorに送信する方法を示すことです。

4. 教えて、聞かないで!

LogKeeperActorによって定義されたプロトコルと動作は単純です。 ロギングメッセージを受信しても、計算を続行できる呼び出し元への応答は生成されません。 この種の相互作用は非同期と呼ばれます。

完全非同期通信もアクター間の相互作用の好ましいタイプであり、TellPatternと呼ばれます。 Akkaライブラリでは非常に重要であるため、開発者はパターンを使用してメッセージを送信するための専用の記号を予約しました:bang “!” (または感嘆符)。

テルパターンを使用するには、アクターはメッセージの送信先のアクターへのアクター参照を取得する必要があります。 このシナリオでは、 apply メソッドでアクターの動作を定義するときに、アクター参照をLogKeeperActorに渡すことができます。

object MicroserviceActor {
  final case class DoSomeStuff[T](stuff: T)

  def apply(logger: ActorRef[LogKeeperActor.Log]): Behavior[DoSomeStuff[_]] = {
    Behaviors.receiveMessage {
      case DoSomeStuff(stuff) =>
        logger ! LogKeeperActor.Info(stuff.toString)
        Behaviors.same
    }
  }
}

MicroserviceActor は、ペイロードを運ぶメッセージを受信し、前に定義した集中型サービスを使用してペイロードをログに記録します。

logger ! LogKeeperActor.Info(stuff.toString)

アクターは、ロガーアクターからの応答の待機をブロックしません。 次のメッセージが来るのを聞いて、それは単に先に進みます。

5. 長所と短所

他のパターンと同様に、Tellパターンには長所と短所があります。 その最も重要な機能は、それが定義するプロトコルの完全な非同期性です。 非ブロッキング操作は、リアクティブプログラミングなどの多くの最新のプログラミングパラダイムの基盤となっています。

ただし、Tell Patternを使用すると、発信者は、メッセージが受信者によって受信されたか処理されたかを認識できなくなります。 たとえば、ネットワークがメッセージを失ったかどうか、または受信者のメールボックスがいっぱいでOutOfMemoryErrorが発生したかどうかを知る方法はありません。

さらに、要求がどの応答にも対応しないような単純なプロトコルを定義できる可能性はほとんどありません。 多くの場合、アクターは別のアクターにメッセージの送信に何らかの結果を返すように要求します。

上記のユースケースでは、AkkaライブラリはAskパターンと呼ばれる別のパターンを実装しています。 このパターンは、Akkaアクターの使用とScala Futureタイプの非同期機能を組み合わせたものです。

さらに、傑作の本 Effective Akka で、Jamie Allenは、Tellパターンのみを使用して要求/応答プロトコルを実装する方法を定義しています。 その最終的な解決策は、カメオパターンとして知られています。

6. 結論

この記事では、Akkaアクター間の最も一般的な相互作用パターンの1つであるTellパターンを確認しました。 その使用例を示し、最後に設計の長所と短所をリストしました。

いつものように、このチュートリアルで使用される完全なコード例は、GitHubから入手できます。