首页 > 解决方案 > 根据请求模式创建子actor

问题描述

我正在尝试使用 Play Framework 创建一个 Web 套接字服务器,其中来自服务器的响应应该是基于请求的同步或异步的。

请求将在 Parent Actor 中处理。根据请求中的动作,将创建子 Actor,并将消息传递给子 Actor 进行处理,并将响应发送回控制器。

有预定义的动作和一些动作的示例请求如下,[,,]

["1234","启动","{"system":"ABCD"}"]

["5678","开始","{"system":"EFGH", "currenTime":"1559548762638"}"]

@Singleton
class RequestController @Inject()(cc: ControllerComponents)(implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {
    def ws = WebSocket.accept[String, String] {req =>
    ActorFlow.actorRef { out =>
      ParentActor.props(out)
    }
  }
}

object ParentActor {
  def props(out: ActorRef) = Props(new ParentActor(out))
}

class ParentActor(out : ActorRef) extends Actor {
    override def receive: Receive = {
         case msg: String => 
         //String split opeartion to find the action.
         //create child actor for the action and pass the message to the child actor
            val action = msg.split(",")[2]
            if("Boot".equals(action)){
                val bootActor: ActorRef = actorSystem.actorOf(Props[BootActor])
                childActor ! msg
            }else if("Start".equals(action)){
                val startActor: ActorRef = actorSystem.actorOf(Props[StartActor])
                startActor ! msg
            }
         case msg: Response => out ! msg
    }
}

case class Response(name:String, msg:String)

class BootActor extends Actor{
    override def receive: Receive = {
        case msg : String => 
        sender() ! Response("ABC",msg) 
    }
}

class StartActor extends Actor{
    override def receive: Receive = {
        case msg : String => 
        sender() ! Response("Efgh",msg) 
    }
}

现在我正在从请求中获取操作并为该操作创建一个子角色并将消息传递给子角色进行处理。但我不确定是否有更好的方法或设计模式来处理请求并创建子角色而不是字符串操作?

标签: scalawebsocketplayframeworkakka

解决方案


首先,您的代码中似乎有一个错字:

if ("Boot".equals(action)) {
  val bootActor: ActorRef = actorSystem.actorOf(Props[BootActor])
  childActor ! msg
} else if ("Start".equals(action)) {
  val startActor: ActorRef = actorSystem.actorOf(Props[StartActor])
  startActor ! msg
}

第一个条件子句中的消息应该发送到bootActor而不是childActor,这在您的代码片段中是未定义的。

另一个问题是您正在使用actorSystem.actorOf创建子演员。此方法创建“顶级”演员,应将其保持在最低限度。与创建的演员在监护演员actorSystem.actorOf的监督下。这与您的代码有关的意思是,当停止时(即,当 WebSocket 关闭时,Play 停止使用的参与者,如此所述),并且不会停止的多个实例,您留下一堆闲置的顶级演员。补救措施是使用创建and的实例:这样做会使这些实例成为.ParentActorActorFlowBootActorStartActorcontext.actorOfBootActorStartActorParentActor

此外,您应该使用==运算符而不是equals方法。

以下是上述更改:

if ("Boot" == action) {
  val bootActor: ActorRef = context.actorOf(Props[BootActor])
  bootActor ! msg
} else if ("Start" == action) {
  val startActor: ActorRef = context.actorOf(Props[StartActor])
  startActor ! msg
}

上面可以稍微简化为以下内容:

val childActor =
  if (action == "Boot") context.actorOf(Props[BootActor])
  else context.actorOf(Props[StartActor])

childActor ! msg

为了进一步简化您的代码,不要创建子角色,在这种情况下这不是必需的。将与actor交互的所有逻辑移动out到单个actor中。


推荐阅读