zio / zio-http

A next-generation Scala framework for building scalable, correct, and efficient HTTP clients and servers
https://zio.dev/zio-http
Apache License 2.0
787 stars 396 forks source link

Graceful shutdown pattern for the "Getting Started" or the "Simple Server" template... #1505

Open chaotic3quilibrium opened 2 years ago

chaotic3quilibrium commented 2 years ago

In anticipation of bootstrapping a new zio-http server, I am using the "Getting Started" template to "grow" towards a solution as I am learning Zio and ZHttp. The current version of the "Getting Started" template looks like this, requiring that I externally "kill" the running process:

import zio.http._
import zio.http.Server
import zio._

object HelloWorld extends ZIOAppDefault {

  val app: HttpApp[Any, Nothing] = Http.collect[Request] {
    case Method.GET -> !! / "text" => Response.text("Hello World!")
  }

  override val run =
    Server.start(8090, app)
}

I would like to modify app by adding a "stop" command, and have the server process perform a "graceful shutdown".

  val app: HttpApp[Any, Nothing] = Http.collect[Request] {
    case Method.GET -> !! / "stop" => ???
    case Method.GET -> !! / "text" => Response.text("Hello World!")
  }

With what might I replace the ??? to achieve that outcome?

Or, if that isn't possible, what is the simplest pattern I could use to implement the desired "graceful shutdown"?

frekw commented 2 years ago

@chaotic3quilibrium something similar to this should work:

  import zhttp._
  import zhttp.service.Server
  import zhttp.http.{ Request, Response }
  import zhttp.http._
  override def run =
    (for {
      shutdownSignal <- Promise.make[Nothing, Unit]
      app             = Http.collectZIO[Request] {
                          case Method.GET -> !! / "stop"   => shutdownSignal.succeed(()).as(Response.text("shutting down"))
                          case Method.GET -> !! / "health" => ZIO.succeed(Response.text("Ok"))
                        }
      server         <- Server.start(8080, app).fork
      _              <- shutdownSignal.await.flatMap(_ => ZIO.sleep(10.seconds) *> server.interrupt)
    } yield ()).provide(
      EventLoopGroup.auto(0),
      ServerChannelFactory.auto
    )
jdegoes commented 2 years ago

Maybe we can add Server#stop method, which stops the server and releases resources early. Of course, this calls into question what Server#install should do in the event the server is already shut down.