It's pretty common when developing a Rust application to restart the application by pressing "CTRL + C/CMD + C". Quick and easy. Get's the job done with minimal hustle. Now for the case that your Rust application is running in production, handling many requests at the same time, running database transactions and may be communicating with other web services etc. What would happen if you did that?
This is where graceful shudown of a Rust application comes in. By gracefully shutting down we ensure that any long running process or worker or incoming requests are gracefully handled/rejected before powering down. This prevents the nasty outcomes above. So, how do you do it in Rust?
I'll just use a simple Axum version but the best is to offer robust graceful shutdown for each OS architecture and also cycling through workers to power each one down prior to shutting down the application. Now, the code to send out our shutdown signal:
async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
info!("Received shutdown signal");
}
Then in your main() function that serves the app you can do something like this:
#[tokio::main]
async fn main() {
...
// Create application state
let app_state = AppState::new(tera, pool, appname);
// Build the router with session middleware
let app = create_router(app_state)
.layer(from_fn_with_state(session_store.clone(), session_middleware));
// Start the server
let listener = match tokio::net::TcpListener::bind(&server_url).await {
Ok(listener) => listener,
Err(e) => {
tracing::error!("Failed to bind to {}: {}", server_url, e);
std::process::exit(1);
}
};
tracing::info!("Server listening on http://{}", server_url);
if let Err(e) = axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await {
tracing::error!("Server error: {}", e);
std::process::exit(1);
}
}
This should work for most simple Rust applications.
WORDCOUNT: 312 words.