優雅地停機

優雅停機指的是在伺服器關閉時,並非立即終止所有連線,而是先停止接收新的請求,同時讓已接受的請求有足夠時間完成處理後再關閉服務。這種方式可以避免請求被突然中斷,提升使用者體驗與系統可靠性。

Salvo 提供了優雅停機的支援,透過 Serverhandle 方法取得伺服器控制權,再呼叫 stop_graceful 方法來實現。呼叫該方法後,伺服器會:

  • 停止接受新的連線請求
  • 等待現有請求處理完成
  • 在指定的逾時時間後(若有提供),強制關閉剩餘連線

以下是一個簡單範例:

main.rs
Cargo.toml
use salvo::prelude::*;
use salvo::server::ServerHandle;
use tokio::signal;

#[tokio::main]
async fn main() {
    // Bind server to port 8698
    let acceptor = TcpListener::new("127.0.0.1:8698").bind().await;
    // Create server instance
    let server = Server::new(acceptor);
    // Get server handle for graceful shutdown
    let handle = server.handle();

    // Listen Shutdown Signal
    tokio::spawn(listen_shutdown_signal(handle));

    // Start serving requests (empty router in this example)
    server.serve(Router::new()).await;
}

async fn listen_shutdown_signal(handle: ServerHandle) {
    // Wait Shutdown Signal
    let ctrl_c = async {
        // Handle Ctrl+C signal
        signal::ctrl_c()
            .await
            .expect("failed to install Ctrl+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        // Handle SIGTERM on Unix systems
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("failed to install signal handler")
            .recv()
            .await;
    };

    #[cfg(windows)]
    let terminate = async {
        // Handle Ctrl+C on Windows (alternative implementation)
        signal::windows::ctrl_c()
            .expect("failed to install signal handler")
            .recv()
            .await;
    };

    // Wait for either signal to be received
    tokio::select! {
        _ = ctrl_c => println!("ctrl_c signal received"),
        _ = terminate => println!("terminate signal received"),
    };

    // Graceful Shutdown Server
    handle.stop_graceful(None);
}

在上面的範例中:

  • server.handle() 取得伺服器的控制權,可用於管理伺服器的生命週期
  • handle.stop_graceful(None) 開始優雅停機流程,None 表示不設定逾時時間,會持續等待所有請求處理完畢
  • 如需設定逾時時間,可傳入 Some(Duration),逾時後將強制關閉剩餘連線

這種方式特別適用於需要在容器環境或雲端平台部署的應用程式,以及需要進行熱更新時確保請求不會被意外中斷的場景。