Создание дочерних актеров на основе шаблона запроса - PullRequest
1 голос
/ 03 июня 2019

Я пытаюсь создать сервер веб-сокетов, используя Play Framework, где ответ от сервера должен быть синхронным или асинхронным в зависимости от запроса.

Запрос будет обработан в родительском акторе. На основе действия в запросе будет создан дочерний актер, и сообщение будет передано дочернему актору для обработки, а ответ будет отправлен обратно в контроллер.

Существуют предопределенные действия, и пример запроса для некоторых действий выглядит следующим образом: [,,]

["1234", "Boot", "{" system ":" ABCD "}"]

["5678", "Start", "{" system ":" EFGH "," currentTime ":" 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) 
    }
}

Сейчас я получаю действие из запроса, создаю дочерний актер для действия и передаю сообщение дочернему актору для обработки. Но я не уверен, что есть лучший способ или шаблон проектирования для обработки запроса и создания дочернего актера вместо операции String?

1 Ответ

1 голос
/ 03 июня 2019

Прежде всего, кажется, что в вашем коде есть опечатка:

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, находятся под наблюдением актера-хранителя .Что это означает в отношении вашего кода, так это то, что когда ParentActor останавливается (т.е. когда WebSocket закрыт, Play останавливает актера, используемого в ActorFlow, как описано здесь ), множественные экземплярыBootActor и StartActor будут не остановлены, оставив вас с кучей бездействующих актеров высшего уровня.Для решения этой проблемы необходимо использовать context.actorOf для создания экземпляров BootActor и StartActor: это делает эти экземпляры потомками ParentActor.

Кроме того, вместо оператора == следует использовать оператор *1026*.equals method.

Вот вышеупомянутые изменения:

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

Для дальнейшего упрощения вашего кодаНе создавайте дочерних актеров, которые в этом случае не нужны.Переместите всю логику взаимодействия с out актером в одного актера.

...