异步终止Akka-Http Web Socket连接

时间:2016-05-03 18:47:21

标签: scala websocket akka-stream akka-http

Akka Http中的Web Socket连接被视为Akka Streams Flow。这似乎对于基本的请求 - 回复非常有用,但是当消息也应该通过websocket推出时,它会变得更加复杂。我服务器的核心看起来像:

lazy val authSuccessMessage = Source.fromFuture(someApiCall)

lazy val messageFlow = requestResponseFlow
    .merge(updateBroadcastEventSource)

lazy val handler = codec
  .atop(authGate(authSuccessMessage))
  .join(messageFlow)

handleWebSocketMessages {
  handler
}

此处,codec是(de)序列化BidiFlowauthGateBidiFlow,它处理授权消息并防止任何消息流出,直到授权成功。成功后,它会发送authSuccessMessage作为回复。 requestResponseFlow是标准的请求 - 回复模式,updateBroadcastEventSource混合了异步推送消息。

我希望能够在某些情况下发送错误消息并正常终止连接,例如错误授权,someApiCall失败或requestResponseFlow处理的错误请求。所以基本上,基本上我似乎希望能够用最后一条消息异步完成messageFlow,即使它的其他成分流仍然存在。

1 个答案:

答案 0 :(得分:0)

使用KillSwitch了解如何执行此操作。

更新版本

旧版本存在的问题是,当堆叠中的BidiFlow阶段(例如我的authGate)触发时,它似乎无法工作。我不确定为什么,但将关闭建模为BidiFlow本身,放在堆栈的更上方,解决了这个问题。

val shutoffPromise = Promise[Option[OutgoingWebsocketEvent]]()

/**
 * Shutoff valve for the connection. It is triggered when `shutoffPromise`
 * completes, and sends a final optional termination message if that
 * promise resolves with one.
 */
val shutoffBidi = {
  val terminationMessageSource = Source
    .maybe[OutgoingWebsocketEvent]
    .mapMaterializedValue(_.completeWith(shutoffPromise.future))

  val terminationMessageBidi = BidiFlow.fromFlows(
    Flow[IncomingWebsocketEventOrAuthorize],
    Flow[OutgoingWebsocketEvent].merge(terminationMessageSource)
  )

  val terminator = BidiFlow
    .fromGraph(KillSwitches.singleBidi[IncomingWebsocketEventOrAuthorize, OutgoingWebsocketEvent])
    .mapMaterializedValue { killSwitch =>
      shutoffPromise.future.foreach { _ => println("Shutting down connection"); killSwitch.shutdown() }
    }

  terminationMessageBidi.atop(terminator)
}

然后我将其应用于codec

val handler = codec
  .atop(shutoffBidi)
  .atop(authGate(authSuccessMessage))
  .join(messageFlow)

旧版

val shutoffPromise = Promise[Option[OutgoingWebsocketEvent]]()

/**
 * Shutoff valve for the flow of outgoing messages. It is triggered when
 * `shutoffPromise` completes, and sends a final optional termination
 * message if that promise resolves with one.
 */
val shutoffFlow = {
  val terminationMessageSource = Source
    .maybe[OutgoingWebsocketEvent]
    .mapMaterializedValue(_.completeWith(shutoffPromise.future))

  Flow
    .fromGraph(KillSwitches.single[OutgoingWebsocketEvent])
    .mapMaterializedValue { killSwitch =>
      shutoffPromise.future.foreach(_ => killSwitch.shutdown())
    }
    .merge(terminationMessageSource)
}

然后handler看起来像:

val handler = codec
  .atop(authGate(authSuccessMessage))
  .join(messageFlow via shutoffFlow)