-
Notifications
You must be signed in to change notification settings - Fork 23
[feat] - Graceful shutdown #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d36c771
38c6008
fc8f9a0
f9e17c9
17d1964
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| [server] | ||
| addr = "0.0.0.0" | ||
| port = 8_080 | ||
| grace_shutdown_secs = 10 | ||
|
|
||
| [db] | ||
| host = "127.0.0.1" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ profile = "test" | |
| [server] | ||
| addr = "127.0.0.1" | ||
| port = 0 | ||
| grace_shutdown_secs = 1 | ||
|
|
||
| [db] | ||
| host = "127.0.0.1" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,9 @@ use self::state::AppState; | |
| use crate::configure::AppConfig; | ||
| use crate::error::AppResult; | ||
| use crate::router::create_router_app; | ||
| use tower_http::timeout::TimeoutLayer; | ||
| use tower_http::trace::TraceLayer; | ||
| use tracing::info; | ||
| pub mod state; | ||
| pub mod worker; | ||
|
|
||
|
|
@@ -20,8 +23,46 @@ impl AppServer { | |
| } | ||
|
|
||
| pub async fn run(self) -> AppResult<()> { | ||
| let router = create_router_app(self.state); | ||
| axum::serve(self.tcp, router).await?; | ||
| let shutdown = self.grace_shutdown_time(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can remove this and directly pass the timeout to the layer. |
||
| let router = create_router_app(self.state) | ||
| .layer(TraceLayer::new_for_http()) // Visibility of the request and response, change as needed. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move the two layers to the |
||
| .layer(TimeoutLayer::new(shutdown)); // Graceful shutdown for hosting services requries N time to complete the request before shutting down. | ||
|
|
||
| axum::serve(self.tcp, router) | ||
| .with_graceful_shutdown(shutdown_signal()) | ||
| .await?; | ||
| Ok(()) | ||
| } | ||
|
|
||
| fn grace_shutdown_time(&self) -> std::time::Duration { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need this function. |
||
| std::time::Duration::from_secs(self.state.config.server.grace_shutdown_secs as u64) | ||
| } | ||
| } | ||
|
|
||
| async fn shutdown_signal() { | ||
| let ctrl_c = async { | ||
| tokio::signal::ctrl_c() | ||
| .await | ||
| .expect("Failed to install CTRL+C signal handler"); | ||
| }; | ||
|
|
||
| #[cfg(unix)] | ||
| let terminate = async { | ||
| tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) | ||
| .expect("Failed to install SIGTERM signal handler") | ||
| .recv() | ||
| .await; | ||
| }; | ||
|
|
||
| #[cfg(not(unix))] | ||
| let terminate = std::future::pending::<()>(); | ||
|
|
||
| tokio::select! { | ||
| _ = ctrl_c => { | ||
| info!("Received Ctrl-C signal. Shutting down..."); | ||
| }, | ||
| _ = terminate => { | ||
| info!("Received SIGTERM signal. Shutting down..."); | ||
| }, | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,18 +17,20 @@ pub async fn join_all(tasks: Vec<Task>) -> AppResult { | |
| tokio::spawn(async { | ||
| if let Err(e) = task.await { | ||
| if let Some(sender) = sender { | ||
| sender | ||
| .send(e) | ||
| .await | ||
| .unwrap_or_else(|_| unreachable!("This channel never closed.")); | ||
| let _ = sender.send(e).await; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this change does not explain why to ignore the result, but the previous code does. It might be better to add a comment : "This channel never closed." |
||
| } else { | ||
| error!("A task failed: {e}."); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| // Explicitly drop the sender to close the channel. | ||
| drop(sender); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! Please change the comment to: "Drop the original unused sender." |
||
|
|
||
| // Return Ok(()) if all futures are completed without error. | ||
| match receiver.recv().await { | ||
| Some(err) => Err(err), | ||
| None => unreachable!("This channel never closed."), | ||
| None => Ok(()), | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change the data type to std::time::Duration: