feat: add graceful shutdown with SIGTERM handling and updater cancellation

This commit is contained in:
NetworkCats
2026-03-04 04:20:35 +08:00
parent 0f1d894d36
commit 50326c205f
5 changed files with 67 additions and 9 deletions
Generated
+20
View File
@@ -329,6 +329,12 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-sink"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
[[package]]
name = "futures-task"
version = "0.3.32"
@@ -1104,6 +1110,7 @@ dependencies = [
"serde_json",
"static-files",
"tokio",
"tokio-util",
"tower",
"tracing",
"tracing-subscriber",
@@ -1434,6 +1441,19 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tower"
version = "0.5.3"
+1
View File
@@ -15,6 +15,7 @@ reqwest = { version = "0.13", default-features = false, features = ["rustls"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
dotenvy = "0.15"
tokio-util = "0.7"
static-files = "0.3.1"
[build-dependencies]
+2
View File
@@ -25,4 +25,6 @@ RUN chown -R rustyip:rustyip /app
USER rustyip
STOPSIGNAL SIGTERM
ENTRYPOINT ["rustyip"]
+31 -5
View File
@@ -1,6 +1,7 @@
use std::path::PathBuf;
use tracing::error;
use tokio_util::sync::CancellationToken;
use tracing::{error, info};
use tracing_subscriber::EnvFilter;
use rustyip::{config, db, handlers, routes, updater};
@@ -32,12 +33,14 @@ async fn main() {
};
let shared_db = db::new_shared(reader);
let cancel = CancellationToken::new();
updater::spawn_updater(
let updater_handle = updater::spawn_updater(
shared_db.clone(),
db_path,
config.db_update_url,
config.db_update_interval_hours,
cancel.clone(),
);
let state = handlers::AppState {
@@ -56,10 +59,33 @@ async fn main() {
.with_graceful_shutdown(shutdown_signal())
.await
.expect("server error");
info!("server stopped, cleaning up background tasks");
cancel.cancel();
let _ = updater_handle.await;
info!("shutdown complete");
}
async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("failed to listen for ctrl+c");
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("failed to listen for ctrl+c");
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to listen for SIGTERM")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
() = ctrl_c => info!("received SIGINT, shutting down"),
() = terminate => info!("received SIGTERM, shutting down"),
}
}
+13 -4
View File
@@ -4,7 +4,9 @@ use std::time::Duration;
use tokio::fs;
use tokio::io::AsyncWriteExt;
use tracing::error;
use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use tracing::{error, info};
use crate::db::{DbReader, SharedDb, load_db};
@@ -55,16 +57,23 @@ pub fn spawn_updater(
db_path: PathBuf,
update_url: String,
interval_hours: u64,
) {
cancel: CancellationToken,
) -> JoinHandle<()> {
tokio::spawn(async move {
let interval = Duration::from_secs(interval_hours * 3600);
loop {
tokio::time::sleep(interval).await;
tokio::select! {
() = tokio::time::sleep(interval) => {}
() = cancel.cancelled() => {
info!("updater task cancelled, stopping");
break;
}
}
if let Err(e) = update_db(&shared_db, &db_path, &update_url).await {
error!("database update failed: {e}");
}
}
});
})
}
async fn update_db(