I have the following code and would like to understand, why it stops immediately, when I execute it:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.io.StdIn
object WebServer {
def main(args: Array[String]) {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.dispatcher
val route =
path("hello") {
get {
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
}
}
As you can see, it is a simple web application and it should not stop to run.
But when I remove the following code:
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
Then it runs forever until I terminated it.
The question is, why without
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
the application is not going to terminate?
Your bindingFuture
gets completed in both cases. If you keep
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
Then unbind
and terminate
get run as soon as the server is listening, which causes your program to finish immediately.
If you omit that code, then your program will keep running until all its threads are done. Because the actor system is running threads in the background and you never terminate it, this will never happen and you'll have to kill your program to stop it.
One way to gracefully shutdown on any key press would be to use this (taken from official documentation)
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
StdIn.readLine() // let it run until user presses return
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
In this case, the StdIn.readLine()
will block the main thread until a key is pressed, at which point the actor system and the http server will be shut down.
If you want to wait for a custom "event" rather than a keypress, you could simply wait on a custom Future
like this:
// Just build a Future that will get completed when you want to shutdown
// This can be done manually using a `Promise[Unit]`, but you might also be using
// a library that will give you a Future to wait on.
val myFuture: Future[Unit] = ???
myFuture.flatMap(_ => bindingFuture)
.flatMap(_.unbind())
.onComplete(_ => system.terminate())