优雅地停机

优雅停机指在服务器关闭时,不是立即终止所有连接,而是先停止接收新的请求,同时让已接受的请求有足够的时间完成处理后再关闭服务。这种方式可以避免请求被突然中断,提高用户体验和系统可靠性。

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),超时后会强制关闭剩余连接

这种方式特别适用于需要在容器环境或云平台上部署的应用,以及需要进行热更新时确保请求不会被意外中断.