Encerramento Gracioso

O encerramento gracioso refere-se ao processo em que, quando um servidor está sendo desligado, ele não termina imediatamente todas as conexões. Em vez disso, primeiro interrompe a aceitação de novas solicitações, permitindo que as solicitações existentes tenham tempo suficiente para concluir seu processamento antes de fechar o serviço. Essa abordagem evita que as solicitações sejam interrompidas abruptamente, melhorando assim a experiência do usuário e a confiabilidade do sistema.

O Salvo fornece suporte para encerramento gracioso através do método handle do Server, que recupera o manipulador do servidor, seguido pela chamada do método stop_graceful para implementar o encerramento. Após invocar esse método, o servidor irá:

  • Parar de aceitar novas solicitações de conexão
  • Aguardar que as solicitações existentes concluam o processamento
  • Fechar forçadamente quaisquer conexões restantes após um tempo limite especificado (se fornecido)

Aqui está um exemplo simples:

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

No exemplo acima:

  • server.handle() recupera o manipulador do servidor, que pode ser usado para controlar o ciclo de vida do servidor
  • handle.stop_graceful(None) inicia o processo de encerramento gracioso, onde None indica que nenhum tempo limite foi definido, significando que o servidor aguardará indefinidamente que todas as solicitações sejam concluídas
  • Para definir um tempo limite, você pode passar Some(Duration), após o qual quaisquer conexões restantes serão fechadas forçadamente

Essa abordagem é particularmente adequada para aplicativos implantados em ambientes de contêineres ou em plataformas de nuvem, bem como para cenários que exigem atualizações em tempo real para garantir que as solicitações não sejam interrompidas inesperadamente.