玩Scala Akka WebSockets改变演员路径

时间:2016-05-17 16:18:25

标签: scala playframework websocket akka

我已经按照Scala Play和Akka演员创建Web套接字的示例:

https://www.playframework.com/documentation/2.5.x/ScalaWebSockets#Handling-WebSockets-with-Akka-Streams-and-actors

恢复时,控制器:

import play.api.mvc._
import play.api.libs.streams._

class Controller1 @Inject() (implicit system: ActorSystem, materializer: Materializer) {

  def socket = WebSocket.accept[String, String] { request =>
    ActorFlow.actorRef(out => MyWebSocketActor.props(out))
  }

演员:

import akka.actor._

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

class MyWebSocketActor(out: ActorRef) extends Actor {
  def receive = {
    case msg: String =>
      out ! ("I received your message: " + msg)
  }
}

创建的actor(每个websocket连接一个)是/ user actor的子级。我创建了3个连接,创建的actor是:

  1. /用户/ $ B
  2. /用户/ $ C
  3. /用户/ $ d
  4. 我想改变演员'名称基于Web套接字消息的字段。我怎么能这样做?。

2 个答案:

答案 0 :(得分:4)

您可以按如下方式设置actor的名称:

  1. 创建一个文件BetterActorFlow.scala

    package your.package
    
    import akka.actor._
    import akka.stream.scaladsl.{Keep, Sink, Source, Flow}
    import akka.stream.{Materializer, OverflowStrategy}
    
    object BetterActorFlow {
    
      def actorRef[In, Out](props: ActorRef => Props, bufferSize: Int = 16, overflowStrategy: OverflowStrategy = OverflowStrategy.dropNew, maybeName: Option[String] = None)(implicit factory: ActorRefFactory, mat: Materializer): Flow[In, Out, _] = {
    
        val (outActor, publisher) = Source.actorRef[Out](bufferSize, overflowStrategy)
                            .toMat(Sink.asPublisher(false))(Keep.both).run()
    
        def flowActorProps: Props = {
          Props(new Actor {
            val flowActor = context.watch(context.actorOf(props(outActor), "flowActor"))
    
            def receive = {
              case Status.Success(_) | Status.Failure(_) => flowActor ! PoisonPill
              case Terminated(_) => context.stop(self)
              case other => flowActor ! other
            }
    
            override def supervisorStrategy = OneForOneStrategy() { case _ => SupervisorStrategy.Stop }
          })
        }
    
        def actorRefForSink =
          maybeName.fold(factory.actorOf(flowActorProps)) { name => factory.actorOf(flowActorProps, name) }
    
        Flow.fromSinkAndSource(Sink.actorRef(actorRefForSink, Status.Success(())), Source.fromPublisher(publisher))
    
      }
    }
    
  2. 使用BetterActorFlow而不是ActorFlow:

    BetterActorFlow.actorRef(out =>
      ChatActor.props(out), 16, OverflowStrategy.dropNew, Some("alicebob"))
    
  3. 这对我有用。创建的actor位于user/alicebob(使用此context.system.actorSelection("user/alicebob")

答案 1 :(得分:3)

根据ActorFlow的源代码,目前无法确定为连接产生的实际actor的名称(第38行):

Sink.actorRef(factory.actorOf(Props(new Actor { ... }) /*, name parameter is omitted */)

但是,ActorFlow.actorRef接受隐式ActorRefFactory,在代码中的所有情况下均为implicit system: ActorSystem。我们知道,有两个最常见的ActorRefFactories:ActorSystemActorContext。您可以修改代码,以便每次接受连接时,另一个虚拟演员将使用您的首选名称(例如myActor1)生成,并将此新演员的context传递给{ {1}}而是。作为回报,连接的参与者将按如下命名:

  • /用户/ myActor1 / $一个
  • /用户/ myActor2 / $一个