Arrêt en douceur

L'arrêt en douceur désigne le processus par lequel, lors de l'arrêt d'un serveur, celui-ci ne met pas immédiatement fin à toutes les connexions. Il cesse d'abord d'accepter de nouvelles requêtes tout en accordant aux requêtes existantes suffisamment de temps pour terminer leur traitement avant de fermer le service. Cette approche évite l'interruption brutale des requêtes, améliorant ainsi l'expérience utilisateur et la fiabilité du système.

Salvo prend en charge l'arrêt en douceur via la méthode handle du Server, qui récupère le gestionnaire du serveur, suivi de l'appel à la méthode stop_graceful pour mettre en œuvre l'arrêt. Après l'appel de cette méthode, le serveur :

  • Cesse d'accepter de nouvelles demandes de connexion
  • Attend que les requêtes existantes terminent leur traitement
  • Ferme de force toute connexion restante après un délai spécifié (si fourni)

Voici un exemple simple :

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);
}

Dans l'exemple ci-dessus :

  • server.handle() récupère le gestionnaire du serveur, qui peut être utilisé pour contrôler le cycle de vie du serveur
  • handle.stop_graceful(None) initie le processus d'arrêt en douceur, où None indique qu'aucun délai n'est défini, ce qui signifie que le serveur attendra indéfiniment que toutes les requêtes se terminent
  • Pour définir un délai, vous pouvez passer Some(Duration), après quoi toute connexion restante sera fermée de force

Cette approche est particulièrement adaptée aux applications déployées dans des environnements conteneurisés ou sur des plateformes cloud, ainsi qu'aux scénarios nécessitant des mises à jour à chaud pour garantir que les requêtes ne soient pas interrompues de manière inattendue.