我有这条路线:
val route = pathPrefix("es") {
path("se") {
post {
entity(as[JsValue]) {
t =>
complete("ok")
}
}
} ~ path("q" / "show") {
get {
complete(q)
}
}
}
当我尝试绑定(bind)它以停止它时(根据 https://doc.akka.io/docs/akka-http/current/routing-dsl/index.html ),我收到一个编译错误:
val bindingFuture = Http().bindAndHandle(route, "0.0.0.0", 9100)
如何停止 HTTP 服务器?目前我可以使用以下命令启动 HTTP 服务器:
startServer("0.0.0.0", port)
但是,我不知道如何使用
startServer
函数来阻止它。更新:我无法按照下面的建议从 Spray 升级到 Akka HTTP(管理,不在我的控制范围内)。
查看
Http().bindAndHandle
,它来自 akka-http-core_2.11-2.4.11.1.jar
。我看到 here ,我需要一个 RouteResult
将其转换为 Flow
。但是我在 RouteResult
中找不到任何 akka-http-core_2.11-2.4.11.1.jar
。 最佳答案
正如其他答案已经指出的那样,您将 Spray 和 Akka HTTP 混为一谈。这两个库是不同的,它们各自的服务器端组件并不意味着共存于同一个应用程序中。如果您无法迁移到取代 Spray 的 Akka HTTP,则从您的项目中删除 Akka HTTP 依赖项并查看 Spray documentation 以获取有关停止 Spray 服务器的信息:
显然您正在使用 SimpleRoutingApp
,这是定义 startServer
方法的地方。此方法没有公开获取对 HttpListener
角色的引用的方法。正如引用的文档所述,您必须向此参与者发送 Http.Unbind
消息才能停止服务器。
一种想法是定义您自己的actor,它可以发送对 HttpListener
的引用:
import akka.actor._
import spray.can.Http
import spray.routing._
object MyActor {
case object GetListener
def props(route: => Route): Props = Props(new MyActor(route))
}
class MyActor(route: => Route) extends HttpServiceActor {
import MyActor._
var httpListener: Option[ActorRef] = None
def routeReceive: Receive = runRoute(route)
def serverLifecycleReceive: Receive = {
case b: Http.Bound =>
println(s"Successfully bound to ${b.localAddress}")
val listener = sender()
httpListener = Some(listener)
case GetListener =>
httpListener.foreach(sender ! _)
}
def receive = routeReceive orElse serverLifecycleReceive
}
然后使用这个 actor 而不是 SimpleRoutingApp
来启动服务器:import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Success
import akka.actor._
import akka.io.IO
import akka.pattern.ask
import akka.util.Timeout
import spray.can.Http
import spray.http._
import spray.routing._
import MyActor
object Main extends App {
implicit val system = ActorSystem()
import system.dispatcher
implicit val timeout = Timeout(5.seconds)
val route = ???
val handler = system.actorOf(MyActor.props(route), name = "handler")
IO(Http) ! Http.Bind(handler, interface = "0.0.0.0", port = 9100)
// run the below code to shut down the server before shutting down the actor system
(handler ? MyActor.GetListener)
.flatMap { case actor: ActorRef => (actor ? Http.Unbind) }
.onComplete {
case Success(u: Http.Unbound) =>
println("Unbinding from the port is done.")
// system.shutdown()
case _ =>
println("Unbinding failed.")
}
}
所有这些都假设您想在(或不)关闭 actor 系统之前显式关闭服务器。如果不是这种情况,您当然可以在不明确停止服务器的情况下关闭 actor 系统。例如,您可以向您的路由添加一个路径来处理此问题(以下代码改编自 Spray 存储库中的 sample applications 之一):object Main extends App with SimpleRoutingApp {
implicit val system = ActorSystem("simple-routing-app")
import system.dispatcher
val route = ...
~ (post | parameter('method ! "post")) {
path("stop") {
complete {
system.scheduler.scheduleOnce(1.second)(system.shutdown())(system.dispatcher)
"Shutting down in 1 second..."
}
}
}
startServer("0.0.0.0", port = 9100) {
route
}.onComplete {
case Success(b) =>
println(s"Successfully bound to ${b.localAddress}")
case Failure(ex) =>
println(ex.getMessage)
system.shutdown()
}
}