在为Web套接字创建流时如何连接输入和输出?

时间:2018-02-13 12:19:24

标签: scala akka actor akka-stream akka-http

假设我有一个演员UserActor知道如何处理传入的消息以及如何发送新消息,我想处理Akka-Http中的Web套接字,所以我创建了Flow[Message, Message, NotUsed]

在这里,我们将新邮件作为JSON获取并将其发送到UserActor。完成源后,我收到SourceDied消息:

    val incomingMessages: Sink[Message, NotUsed] =
      Flow[Message]
        .mapAsync(1) {
          case TextMessage.Strict(text)  => Future.successful(text)
          case TextMessage.Streamed(msg) => msg.runFold("")(_ + _)
        }
        .map(decode[IncomingMessage])
        .collect { case Right(msg) => msg }
        .map(_.toMessage)
        .to(Sink.actorRef[ChatMessage](userActor, SourceDied))

我在out注册了UserActor,我们会发送消息:

    val outgoingMessages: Source[Message, NotUsed] =
      Source
        .actorRef[ChatMessage](20, OverflowStrategy.fail)
        .mapMaterializedValue { outActor =>
          userActor ! Connect(outActor)
          NotUsed
        }
        .map((x: ChatMessage) => OutgoingMessage.fromMessage(x))
        .map((outMsg: OutgoingMessage) => TextMessage(outMsg.asJson.toString))

    Flow.fromSinkAndSource(incomingMessages, outgoingMessages)

但是,UserActor是每个用户一个,每个用户可以同时打开多个套接字。所以我只是收集outs来设置UserActor内部并向每个人发送信息。它很好用。

但是,当来源向我发送终止消息时(SourceDied在我的情况下),我不知道out这个source被分配到哪个 - 我无法确定哪个out我应该告知完成情况,然后从我的outs集中删除。

2 个答案:

答案 0 :(得分:1)

一个想法是更改Flow以为每个连接获取唯一标识符:

def websocketFlow(connectionId: String): Flow[Message, Message, NotUsed] = {
  val incomingMessages: Sink[Message, NotUsed] =
    ...
    .to(Sink.actorRef[ChatMessage](userActor, SourceDied(connectionId)))

  val outgoingMessages: Source[Message, NotUsed] =
    Source
      .actorRef[ChatMessage](20, OverflowStrategy.fail)
      .mapMaterializedValue { outActor =>
        userActor ! Connect(connectionId, outActor)
        NotUsed
      }
      ...

  Flow.fromSinkAndSource(incomingMessages, outgoingMessages)
}

显然,您需要调整SourceDiedConnect消息以包含连接ID(例如,在这种情况下,可以使用类似java.util.UUID.randomUUID.toString的内容生成)。然后在UserActor中,将Set替换为Map,其中的键是连接ID。使用Map可以查找连接actor并根据需要将其删除。

答案 1 :(得分:0)

因此,正如我从@chunjef的回答中所理解的那样,没有直接的方法可以做到这一点。我个人决定不使用随机ID生成,而是在套接字和UserActor之间再使用一个Actor。

基本上,现在我有SocketHandlerActor(userActor: ActorRef)替换套接字创建部分中的UserActor。它只是连接到UserActor并在套接字和UserActor之间发送所有消息。

但是,当SocketHandlerActor收到SourceDied条信息时,它只会执行操作,然后使用out杀死PoisionPill和自我。收到UserActor消息后,outs会将其从自己的Termination列表中删除。