我想做以下事情:
object SprayTest extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
import system.dispatcher
startServer(interface = "0.0.0.0", port = 8080) {
post {
path("configNetwork") {
entity(as[Config]) { config =>
complete {
// has a response indicating "OK"
// also, restarts the network interface
handleConfig(config)
}
}
}
}
}
}
问题是handleConfig
重新初始化网络接口,因此访问此端点的远程主机永远不会收到响应。
解决此问题的一种方法是在一个单独的线程中运行handleConfig
并立即完成请求,例如" OK"。这不是一个好的解决方案,但是因为它在未来和请求完成之间引入了竞争条件(如果未来在同一个线程"执行上下文中执行,它总是会失败)。
因此,理想的解决方案是将回调附加到"写入响应"在成功发送响应之后,在那里执行网络重新初始化。有没有办法在喷雾框架中实现这一目标?
作为竞争条件的简单示例,请考虑以下两个示例:
object SprayTest extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
import system.dispatcher
startServer(interface = "0.0.0.0", port = 8080) {
post {
path("configNetwork") {
entity(as[Config]) { config =>
ctx =>
ctx.complete("OK")
System.exit(0) // empty response due to this executing before response is sent
}
}
}
}
}
object SprayTest extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
import system.dispatcher
startServer(interface = "0.0.0.0", port = 8080) {
post {
path("configNetwork") {
entity(as[Config]) { config =>
ctx =>
ctx.complete("OK")
Thread.sleep(1000)
System.exit(0) // response is "OK" because of the sleep
}
}
}
}
}
答案 0 :(得分:0)
您可以使用withAck
上的HttpResponse
方法在线路上发送回复时接收通知。下面是代码中的内容示意图,但我怀疑如果您重新配置低级网络接口,则需要实际关闭http侦听器并重新绑定。
case object NetworkReady
class ApiManager extends HttpServiceActor with Directives {
override def receive: Receive = networkReady
private def networkReady: Receive = runRoute(routes) orElse networkManagementEvents
private def networkManagementEvents: Receive = {
case Config =>
context.become(reconfiguringNetwork)
magicallyReconfigureNetwork pipeTo self
}
private def magicallyReconfigureNetwork: Future[NetworkReady] = ???
private def reconfiguringNetwork: Receive = {
case NetworkReady => context.become(networkReady)
case _: HttpRequest => sender() ! HttpResponse(ServiceUnavailable)
case _: Tcp.Connected => sender() ! Tcp.Close
}
private def routes: Route = {
(post & path("configNetwork") & entity(as[Config])) { config =>
complete(HttpResponse(OK).withAck(config))
}
}
}