アッカで俳優を発見する
1. 序章
このチュートリアルでは、Akkaでアクターを検出する方法を見ていきます。 まず、2つのアクターを登録し、直接のアクター参照を使用する代わりに、ServiceKeysを使用してそれらを取得します。
2. 依存関係
始める前に、build.sbtファイルの依存関係にAkkaを追加します。
libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % "2.6.15"
libraryDependencies += "org.slf4j" % "slf4j-simple" % "1.7.21"
3. アクターの発見可能性を使用する理由
通常、Akkaアクターは、次の5つの方法のいずれかで取得された参照を使用して他のアクターと対話します。
- 別のアクター内にアクターを作成する
- アクター参照をアクターコンストラクターに渡す
- アクター名を使用してactorSelection関数からアクター参照を取得する
- メッセージでアクター参照を送信する
- 送信者参照を使用して、別のメッセージへの返信としてメッセージを送信する
アクターの検出可能性は、サービスキーとそれに対応するアクターが登録されている限り、アクターシステムに存在するアクターを取得するための集中型ソリューションを提供します。
4. アクターの発見可能性はどのように機能しますか?
Akkaには、 aレセプションと呼ばれる組み込みのアクターがあります。これは、アクターから登録要求を受信し、アクター参照を要求したアクターにアクター参照を送信します。 これは、ServiceKeyからアクター参照へのルックアップテーブルマッピングとして機能します。
受付係はそれ自体がアクターであるため、メッセージを使用して通信します。 受付係の応答もメッセージです。 したがって、要求側のアクターは、アクターのリスト(受付係からの応答)を受信できるように準備する必要があります。
5. 例
この例では、加算や乗算などの数学計算を実行するアクターシステムを実装します。 タイプOperations.Calculateのメッセージを受信し、計算結果をログに記録する個別のアクターとして、両方の操作を実装します。
5.1. アクターの定義
AdditionおよびMultiplicationアクターを定義しましょう。
object Addition {
def apply(): Behavior[Operations.Calculate] = Behaviors.setup { context =>
Behaviors.receiveMessage[Operations.Calculate] {
message =>
context.log.info(s"${message.a} + ${message.b} = ${message.a + message.b}")
Behaviors.same
}
}
}
object Multiplication {
def apply(): Behavior[Operations.Calculate] = Behaviors.setup { context =>
Behaviors.receiveMessage[Operations.Calculate] {
message =>
context.log.info(s"${message.a} * ${message.b} = ${message.a * message.b}")
Behaviors.same
}
}
}
5.2. アクターの登録
次に、これらのアクターのインスタンスを作成します。 サービスキーを定義し、アクターインスタンスを生成し、それらをReceptionistアクターに登録する追加のアクターを使用します。 アクターは自分自身を登録できるため、登録コードをAdditionおよびMultiplicationアクターに移動できることに注意してください。
アクターを作成するためのOperationsオブジェクトを定義しましょう。
object Operations {
final case class Setup()
final case class Calculate(a: Int, b: Int)
val AdditionKey = ServiceKey[Operations.Calculate]("addition")
val MultiplicationKey = ServiceKey[Operations.Calculate]("multiplication")
def apply(): Behavior[Setup] = Behaviors.setup{ context =>
Behaviors.receiveMessage[Setup]{ _ =>
context.log.info("Registering operations...")
val addition = context.spawnAnonymous(Addition())
context.system.receptionist ! Receptionist.Register(Operations.AdditionKey, addition)
context.log.info("Registered addition")
val multiplication = context.spawnAnonymous(Multiplication())
context.system.receptionist ! Receptionist.Register(Operations.MultiplicationKey, multiplication)
context.log.info("Registered multiplication")
Behaviors.same
}
}
}
上記のコードでは、最初に、アクター間で渡されるメッセージとして使用する2つのケースクラスを定義しました。 次に、アクターサービスキーを定義します。 後で、サービスキーを使用して、受付係からアクター参照を取得します。
アクター本体では、 Setup リクエストを取得するまで待機してから、アクターインスタンスを生成し、Receptionistに登録します。
5.3. アクターの取得と使用
これで、 Calculator アクターを実装する準備が整いました。これは、前に定義したアクターを使用します。
object Calculator {
sealed trait Command
final case class Calculate(operation: String, a: Int, b: Int) extends Command
final case class CalculateUsingOperation(operation: ActorRef[Operations.Calculate], a: Int, b: Int) extends Command
...
}
最初のステップでは、アクターによって取得されたメッセージの階層を作成する必要があります。 アクターは、操作名が付いた Calculate メッセージを受信し、受付係にアクター参照を要求し、それを自分自身に送信されるメッセージとして受信します。 したがって、2番目のメッセージには、操作名の代わりにアクター参照が含まれています。
コードを単純化するために、操作名をServiceKeyにマッピングするヘルパー関数を実装します。
private def getKey = (operationName: String) => {
operationName match {
case "addition" => Operations.AdditionKey
case "multiplication" => Operations.MultiplicationKey
}
}
次に、SetupリクエストをOperationsアクターに送信するロジックを実装します。 これにより、追加と乗算が生成され、受付係に登録され、アクター参照を要求して計算リクエストを処理し、計算を要求するアクター参照。
この実装をapply関数に追加しましょう。
def apply(): Behavior[Command] = Behaviors.setup{ context =>
context.spawn(Operations(), "operations") ! Operations.Setup()
implicit val timeout: Timeout = Timeout.apply(100, TimeUnit.MILLISECONDS)
Behaviors.receiveMessagePartial[Command] {
case Calculate(operation, a, b) => {
context.log.info(s"Looking for implementation of ${operation}")
val operationKey = getKey(operation)
context.ask(
context.system.receptionist,
Receptionist.Find(operationKey)
) {
case Success(listing) => {
val instances = listing.serviceInstances(operationKey)
val firstImplementation = instances.iterator.next()
CalculateUsingOperation(firstImplementation, a, b)
}
}
Behaviors.same
}
case CalculateUsingOperation(operation, a, b) => {
context.log.info("Calculating...")
operation ! Operations.Calculate(a, b)
Behaviors.same
}
}
}
5.4. アクターシステムの起動
これで、アクターシステムを開始し、2つの計算を要求できます。
object MathActorDiscovery extends App {
val system: ActorSystem[Calculator.Calculate] = ActorSystem(Calculator(), "calculator")
system ! Calculator.Calculate("addition", 3, 5)
system ! Calculator.Calculate("multiplication", 3, 5)
system.terminate()
}
6. 俳優登録の購読
受付係にアクター参照を要求することに加えて、アクターが指定されたサービスキーに登録するたびにメッセージを受け取ることもできます。 この機能を使用するには、Receiveist.SubscribeメッセージをReceptionistに送信する必要があります。
ServiceKey の両方にサブスクライブし、アクターパスをログに記録するRegistrationListenerアクターを実装しましょう。
object RegistrationListener {
def apply(): Behavior[Receptionist.Listing] = Behaviors.setup {
context =>
context.system.receptionist ! Receptionist.Subscribe(Operations.AdditionKey, context.self)
context.system.receptionist ! Receptionist.Subscribe(Operations.MultiplicationKey, context.self)
Behaviors.receiveMessage[Receptionist.Listing] {
listing =>
val key = listing.key
listing.getServiceInstances(key).forEach{ reference =>
context.log.info(s"Registered: ${reference.path}")
}
Behaviors.same
}
}
}
Calculatorセットアップでアクターをスポーンすることを忘れないでください。
context.spawnAnonymous(RegistrationListener())
2つの追加のログメッセージが表示されます。
[calculator-akka.actor.default-dispatcher-7] INFO com.baeldung.akka.RegistrationListener$ - Registered: akka://calculator/user/operations/$a
[calculator-akka.actor.default-dispatcher-7] INFO com.baeldung.akka.RegistrationListener$ - Registered: akka://calculator/user/operations/$b
7. アクターの登録解除
アクターが不要になったら、アクターシステムからアクターインスタンスを削除する前に、アクターの登録を解除する必要があります。
context.system.receptionist ! Receptionist.Deregister(Operations.AdditionKey, context.self)
Receptionist はアクターの登録をすぐに解除しないため、しばらくの間、登録解除されたアクターのアクター参照を返す可能性があることに注意してください。
8. 結論
この記事では、Akkaのアクター検出機能の使用方法、アクター参照の登録、登録のサブスクライブ、 ServiceKey によるアクターの取得、およびアクターの登録解除の方法を学習しました。
いつものように、私たちのアプリケーションのソースコードはGitHubでから入手できます。