如何使用akka流重构此代码。

时间:2016-03-14 17:51:42

标签: playframework akka playframework-2.5

这个想法是保持频道打开以便以后使用它。在playframework 2.5.x中,文档说你必须使用akka流,但没有说明如何实现这个例子。有人可以帮帮我吗?

import play.api.mvc._
import play.api.libs.iteratee._
import play.api.libs.concurrent.Execution.Implicits.defaultContext

def socket =  WebSocket.using[String] { request =>

  // Concurrent.broadcast returns (Enumerator, Concurrent.Channel)
  val (out, channel) = Concurrent.broadcast[String]

  // log the message to stdout and send response back to client
  val in = Iteratee.foreach[String] {
    msg => println(msg)
      // the Enumerator returned by Concurrent.broadcast subscribes to the channel and will
      // receive the pushed messages
      channel push("I received your message: " + msg)
  }
  (in,out)
}

3 个答案:

答案 0 :(得分:4)

你必须做这样的事情!

val (subscriber, publisher)=Source.asSubscriber[String]
      .toMat(Sink.asPublisher[String](fanout = true))(Keep.both).run()

def websocketAction=WebSocket.accept { requestHeader =>
    Flow.fromSinkAndSource(Sink.fromSubscriber(subscriber),Source.fromPublisher(publisher))
}

第一部分将根据接收器和流程创建您需要推送消息并接收消息的对象(订阅发布者)。

最后,您将为使用该代码Flow.fromSinkAndSource收到的每个websocket请求创建一个流程......有关Akka Streams(Source s,{{{{{{ 1}} s和Sink s)是它们代表流的形状,但不代表流本身......当你实现它们时流程就会流动(方法Flow或{{1 }})。现在......播放在使用WebSockets时接收runWith s(使用服务器发送事件时)或run s。它们还没有实现......所以你需要实现它们(第一行)然后再创建一个Flow! (websocketAction行)

对不起,如果我不够清楚,但是使用该代码,它会起作用。

答案 1 :(得分:1)

我终于找到了使用Actors的解决方案。我发现了这个:

def conect = WebSocket.accept[JsValue, JsValue] {request => 
  ActorFlow.actorRef(out => UserWebSocket.props(out, users))
}

然后我查看了ActorFlow.actorRef的源代码: https://github.com/playframework/playframework/blob/2.5.0/framework/src/play-streams/src/main/scala/play/api/libs/streams/ActorFlow.scala

并提出了这个解决方案:

import javax.inject._
import play.api.Configuration
import play.api.mvc._
import scala.concurrent._

import akka.stream.{Materializer, OverflowStrategy}
import akka.stream.scaladsl.{Flow, Keep, Sink, Source}
import akka.actor._

class UserActor(out: ActorRef) extends Actor {
  def receive = {
    // receives messages from client browser here
    // out is actor that will send messages back to client(s)
    case msg: String => out ! "Received message "+msg
  }
}
object UserActor {
  def props(out: ActorRef) = Props(new UserActor(out))
}

@Singleton
class NotificationController @Inject()(val config:Configuration)
                          (implicit ec: ExecutionContext, actorSystem:ActorSystem, materializer: Materializer) extends Controller {

  // outActor can be used to send messages to client(s)
  // Sink.asPublisher(true) makes this a broadcast channel (multiple clients can connect to this channel, and messages sent to outActor are broadcast to all of them).  Use Sink.asPublisher(false) to create a unicast channel.
  val (outActor, publisher) = Source.actorRef[String](99, OverflowStrategy.dropNew)
        .toMat(Sink.asPublisher(true))(Keep.both).run()


  def flowsocket = WebSocket.accept[String, String] {request =>
    val aflow:Flow[String, String, _] = {

        val sink = Sink.actorRef( actorSystem.actorOf(UserActor.props(outActor)), akka.actor.Status.Success(()) )

        val source = Source.fromPublisher(publisher)

        Flow.fromSinkAndSource(
            sink, source
        )
    }
    aflow
  }

}

我已经修改了我的解决方案,以更全面地接受Actor模型。我现在有一个“UsersBroadcastActor”,它是一个单独的演员,所有其他“UserActor”连接到并可以通过它进行通信:

lazy val broadcastActorRef = actorSystem.actorOf(Props[UsersBroadcastActor])

def flowsocket = WebSocket.accept[JsValue, JsValue] { request =>
    ActorFlow.actorRef(out => UserActor.props(out, broadcastActorRef))
}

当实例化UserActor时,在其preStart()方法中,它向broadcastActorRef发送订阅消息,该消息将保存对“订阅”它的所有UserActors的引用。我可以发送一条消息给broadcastActorRef,并将它转发给每个UserActors。如果您还想要此解决方案的完整代码示例,请与我们联系。

答案 2 :(得分:-1)

我认为您只是在寻找如何使用Play 2.5和Akka Streams流程进行Echo websocket连接。

这应该可以解决问题

  def socket = WebSocket.accept[String, String] { request =>
    Flow[String]
      .map(msg => "I received your message: " + msg)
  }