使用akka-http回答特定的客户,还支持广播

时间:2019-02-01 18:10:06

标签: scala akka akka-http

使用akka-http库创建服务器让我有些迷茫。我需要建立的联系如下:

  • 有一台服务器和n个客户端(n <5)
  • 有时客户端向服务器发送命令,服务器会评估/授权命令并回答客户端
  • 服务器不断向所有客户端广播消息

鉴于:

  • 我的服务器需要管理通过websocket连接的多个“会话”

这是我的websocket端点:

path("socket") {
  handleWebSocketMessages(listen())
}

这里是listen()方法:

// stores offers to broadcast to all clients
private var offers: List[TextMessage => Unit] = List()

def listen(): Flow[Message, Message, NotUsed] = {
  val inbound: Sink[Message, Any] = Sink.foreach(m => /* handle the message */) // (*)
  val outbound: Source[Message, SourceQueueWithComplete[Message]] =
    Source.queue[Message](16, OverflowStrategy.fail)

  Flow.fromSinkAndSourceMat(inbound, outbound)((_, outboundMat) => {
    offers ::= outboundMat.offer
    NotUsed
  })
}

def sendText(text: String): Unit = {
  for (connection <- offers) connection(TextMessage.Strict(text))
}

使用这种方法,我可以注册多个客户端并使用sendText(text: String)方法来回答它们。但是,有一个大问题:评估了它的命令后,如何仅回答特定的客户端。 (请参见(*)

[让我烦恼的另一件事是offers是一个变量,以纯FP方式进行编程时这似乎是错误的,但我可以接受,如果其余的都可以工作]

编辑:

为了详细说明,我基本上需要能够实现如下所示的方法:

def onMessageReceived(m: Message, answer: TextMessage => Unit): Unit = {
  val response: TextMessage = handleMessage(m)
  answer(response)
}

但是我无法弄清楚在我的websocket流中该方法的调用位置。

1 个答案:

答案 0 :(得分:0)

我不确定这是否可行,但这似乎行得通:

var actors: List[ActorRef] = Nil

private def wsFlow(implicit materializer: ActorMaterializer): Flow[ws.Message, ws.Message, NotUsed] = {
    val (actor, source) = Source.actorRef[String](10, akka.stream.OverflowStrategy.dropTail)
      .toMat(BroadcastHub.sink[String])(Keep.both)
      .run()

    actors = actor :: actors

    val wsHandler: Flow[ws.Message, ws.Message, NotUsed] =
      Flow[ws.Message]
        .merge(source)
        .map {
          case TextMessage.Strict(tm) => handleMessage(actor, tm)
          case _ => TextMessage.Strict("Ignored message!")
        }
    wsHandler
  }

  def broadcast(msg: String): Unit = {
    actors.foreach(_ ! TextMessage.Strict(msg))
  }