Skip to content

Commit 8999579

Browse files
committed
add v1 graceful shutdown guide
1 parent b0b274c commit 8999579

File tree

3 files changed

+115
-2
lines changed

3 files changed

+115
-2
lines changed

_data/stable.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- hello-world
1919
- echo
2020
- middleware
21+
- graceful-shutdown
2122

2223
- title: Client
2324
path: "/client"

_legacy/server/graceful-shutdown.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
---
22
title: Gracefully Shutdown a Server
33
layout: guide
4-
redirect_from:
5-
- /guides/server/graceful-shutdown
64
---
75

86
hyper `Server`s have the ability to "gracefully" shutdown. This means stopping to accept new requests, and shutting down once all in-progress requests have completed.

_stable/server/graceful-shutdown.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
title: Gracefully Shutdown a Server
3+
layout: guide
4+
redirect_from:
5+
- /guides/server/graceful-shutdown
6+
---
7+
8+
hyper's server connections have the ability to initiate a graceful shutdown. A common desire is to coordinate graceful shutdown across all active connections. This is what we'll tackle in this guide.
9+
10+
> A **graceful shutdown** is when a connection stops allowing _new_ requests, while allowing currently in-flight requests to complete.
11+
12+
In order to do, we'll need several pieces:
13+
14+
1. A signal for when to start the shutdown.
15+
2. An accept loop handling newly received connections.
16+
3. A watcher to coordinate the shutdown.
17+
18+
## Determine a shutdown signal
19+
20+
You can use any mechanism to signal that graceful shutdown should begin. That could be an process signal handler, a timer, a special HTTP request, or anything else.
21+
22+
We're going to use a `CTRL+C` signal handler for this guide. Tokio has simple support for making one, let's try:
23+
24+
```rust
25+
# extern crate tokio;
26+
async fn shutdown_signal() {
27+
// Wait for the CTRL+C signal
28+
tokio::signal::ctrl_c()
29+
.await
30+
.expect("failed to install CTRL+C signal handler");
31+
}
32+
# fn main() {}
33+
```
34+
35+
## Modify your server accept loop
36+
37+
> **Unstable**: The code discussed in this guide is in `hyper-util`,
38+
> which is not as stable as that which is in `hyper`. It is production
39+
> ready, but changes may come more frequently.
40+
41+
We're assuming you have an accept loop for your server, similar to what was shown in the [Hello World](hello-world.md) guide. So, we're just going to modify it here:
42+
43+
```rust
44+
# extern crate hyper;
45+
# extern crate http_body_util;
46+
# extern crate hyper_util;
47+
# extern crate tokio;
48+
# mod no_run {
49+
# use std::convert::Infallible;
50+
# use std::net::SocketAddr;
51+
#
52+
# use http_body_util::Full;
53+
# use hyper::body::Bytes;
54+
# use hyper::server::conn::http1;
55+
# use hyper::service::service_fn;
56+
# use hyper::{Request, Response};
57+
# use hyper_util::rt::TokioIo;
58+
# use tokio::net::TcpListener;
59+
# async fn shutdown_signal() {}
60+
# async fn hello(
61+
# _: Request<hyper::body::Incoming>,
62+
# ) -> Result<Response<Full<Bytes>>, Infallible> {
63+
# Ok(Response::new(Full::new(Bytes::from("Hello World!"))))
64+
# }
65+
# async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
66+
# let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
67+
let listener = TcpListener::bind(addr).await?;
68+
// specify our HTTP settings (http1, http2, auto all work)
69+
let mut http = http1::Builder::new();
70+
// the graceful watcher
71+
let graceful = hyper_util::server::graceful::GracefulShutdown::new();
72+
// when this signal completes, start shutdown
73+
let mut signal = std::pin::pin!(shutdown_signal());
74+
75+
// Our server accept loop
76+
loop {
77+
tokio::select! {
78+
Ok((stream, _addr)) = listener.accept() => {
79+
let io = TokioIo::new(stream);
80+
let conn = http.serve_connection(io, service_fn(hello));
81+
// watch this connection
82+
let fut = graceful.watch(conn);
83+
tokio::spawn(async move {
84+
if let Err(e) = fut.await {
85+
eprintln!("Error serving connection: {:?}", e);
86+
}
87+
});
88+
},
89+
90+
_ = &mut signal => {
91+
eprintln!("graceful shutdown signal received");
92+
// stop the accept loop
93+
break;
94+
}
95+
}
96+
}
97+
98+
// Now start the shutdown and wait for them to complete
99+
// Optional: start a timeout to limit how long to wait.
100+
101+
tokio::select! {
102+
_ = graceful.shutdown() => {
103+
eprintln!("all connections gracefully closed");
104+
},
105+
_ = tokio::time::sleep(std::time::Duration::from_secs(10)) => {
106+
eprintln!("timed out wait for all connections to close");
107+
}
108+
}
109+
# Ok(())
110+
# }
111+
# }
112+
# fn main() {}
113+
```
114+

0 commit comments

Comments
 (0)