Akka Typed:回复从不同参与者发送的消息(告诉)

dav*_*vid 0 scala akka akka-typed

使用 Akka classic,人们可以轻松实现一项服务(作为参与者:ServiceActor)

  • 接收来自不同参与者的请求(即不同类型的参与者:RequestorA、RequestorB等),
  • 处理请求,
  • 向请求者发回确认。

RequestorA 向 ServiceActor 发送 RequestMessage ServiceActor 向 RequestorA 发回 Acknowledgment

类似地,RequestorB 向 ServiceActor 发送 RequestMessage,ServiceActor 向 RequestorB 发回 Acknowledgment

无论Requestor是什么,都只有一种类型的RequestMessage和Acknowledgment,并且所有RequestMessage都由ServiceActor以相同的方式处理。

使用 Akka Typed 实现类似功能的方法是什么?既然请求消息必须包含显式的replyTo:ActorRef[RequestorA.Message],是否有办法避免为每个请求者实现不同的RequestMessage?

  • RequestFromRequestorA(回复:ActorRef[RequestorA.Message])
  • RequestFromRequestorB(回复:ActorRef[RequestorB.Message])

同样,是否有办法避免向每种类型的请求者发回不同类型的确认?

Lev*_*sey 7

通常在 Akka Typed 中,响应消息由处理请求的 actor 的协议定义,例如(在 Scala 中):

object MyActor {
  sealed trait Command

  case class Request(replyTo: ActorRef[Response]) extends Command

  sealed trait Response

  // and so forth
}
Run Code Online (Sandbox Code Playgroud)

然后,发送 的参与者就有责任Request安排处理Response。如果请求参与者恰好只是为了发送请求而存在,它可以将自己定义为Behavior[Response],但在更一般的情况下,可以使用几种策略。

参与者可以设置消息适配器:

val responseRef = context.messageAdapter { response =>
  // convert to this actor's message protocol
  ???
}

otherActor ! MyActor.Request(responseRef)
Run Code Online (Sandbox Code Playgroud)

如果这样做,通常最好设置responseRef唯一一次(例如在 中Behaviors.setup)。

如果参与者只想执行一次请求,或者只期望每个请求得到一个响应(并且期望在有限的时间内得到该响应),那么“询问模式”可能会更清晰:

implicit val timeout: Timeout = 15.seconds
context.ask(otherActor, MyActor.Request(_)) {
  case Success(resp) =>
    // convert to this actor's message protocol
    ???

  case Failure(ex) =>
    // convert to this actor's message protocol
    ???
Run Code Online (Sandbox Code Playgroud)

这确实需要您定义显式超时并处理未及时收到响应的情况。注入ActorRefRequest消息是短暂的:除了发送给它的第一条消息之外,它不会接收任何消息,也不会在超时后接收消息。

也可以将 actor 的行为定义为 a Behavior[Any](这就像“经典”的无类型 actor),但不将该事实暴露在 actor 之外。在 Scala 中,由于逆变,这相当简单且安全:

Behaviors.setup[Any] { context =>
  // context.self is an ActorRef[Any], which by contravariance is usable as any type of ActorRef
  otherActor ! MyActor.Request(context.self)

  Behaviors.receiveMessage {
    case resp: MyActor.Response =>
      // handle response directly
      ???

    case _ =>
      Behaviors.unhandled
  }
}.narrow[Command]
Run Code Online (Sandbox Code Playgroud)

生成的ActorRef将是 an ActorRef[Command](即仅承诺处理Command),但参与者能够决定承诺处理它想要处理的任何消息(即使不是 a Command)。当涉及到该参与者向自身发送消息时,这确实会使您脱离编译器的帮助;此外,这可能是一个有点晦涩的模式,因此它的使用应该得到很好的评论。