在scala喷涂服务器成功发送响应后附加回调以运行

时间:2014-06-11 02:15:53

标签: scala spray

我想做以下事情:

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
        }
      }
    }
  }
}

1 个答案:

答案 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))
    }
  }

}