You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust & Tokio:如何处理SIGINT之外的更多终止信号(如SIGQUIT)

问题

我有一个基于Tokio/Tonic提供gRPC服务、通过Warp暴露指标的微服务,目前仅能在收到SIGINT信号(即终端执行kill命令)时正常停机。相关代码如下:

// Sigint signal handler that closes the DB connection upon shutdown
let signal = grpc_sigint(dbm.clone());

// Construct health service for gRPC server
let (mut health_reporter, health_svc) = tonic_health::server::health_reporter();
health_reporter.set_serving::<JobRunnerServer<MyJobRunner>>().await;

// Build gRPC server with health service and signal sigint handler
let grpc_server = TonicServer::builder()
    .add_service(grpc_svc)
    .add_service(health_svc)
    .serve_with_shutdown(grpc_addr, signal);

当前的SIGINT处理逻辑如下:

async fn grpc_sigint(dbm: DBManager) {
    let _ = signal(SignalKind::terminate())
        .expect("failed to create a new SIGINT signal handler for gRPC")
        .recv()
        .await;

    // Shutdown the DB connection.
    dbm.close_db().await.expect("Failed to close database connection");

    println!("gRPC shutdown complete");
}

该逻辑可正常工作,但服务无法捕获Ctrl+C及SIGQUIT等其他信号,且因需跨平台运行,仅处理SIGINT并不足够。如何修改信号处理逻辑,使其能捕获SIGINT、Ctrl+C、SIGQUIT等多种停机信号并触发优雅停机?


解决方案

要实现跨平台的多信号捕获,可通过tokio::signal同时监听多种信号类型,用select!宏等待任意一个信号触发,再执行统一的优雅停机逻辑。

修改后的信号处理函数

use tokio::signal::{unix::{signal, SignalKind}, ctrl_c};

async fn grpc_shutdown_handler(dbm: DBManager) {
    // 监听类Unix系统的SIGINT、SIGQUIT、SIGTERM信号
    let mut sigint = signal(SignalKind::interrupt())
        .expect("failed to create SIGINT signal handler");
    let mut sigquit = signal(SignalKind::quit())
        .expect("failed to create SIGQUIT signal handler");
    let mut sigterm = signal(SignalKind::terminate())
        .expect("failed to create SIGTERM signal handler");

    // 同时等待跨平台Ctrl+C或任意Unix信号触发
    tokio::select! {
        _ = sigint.recv() => println!("Received SIGINT, starting shutdown..."),
        _ = sigquit.recv() => println!("Received SIGQUIT, starting shutdown..."),
        _ = sigterm.recv() => println!("Received SIGTERM, starting shutdown..."),
        _ = ctrl_c() => println!("Received Ctrl+C, starting shutdown..."),
    }

    // 执行统一的优雅停机操作
    dbm.close_db().await.expect("Failed to close database connection");
    println!("gRPC shutdown complete");
}

调用修改后的处理函数

只需将原grpc_sigint替换为新的grpc_shutdown_handler即可:

// 替换原signal变量的初始化
let signal = grpc_shutdown_handler(dbm.clone());

// 后续gRPC服务构建逻辑保持不变
let (mut health_reporter, health_svc) = tonic_health::server::health_reporter();
health_reporter.set_serving::<JobRunnerServer<MyJobRunner>>().await;

let grpc_server = TonicServer::builder()
    .add_service(grpc_svc)
    .add_service(health_svc)
    .serve_with_shutdown(grpc_addr, signal);

关键说明

  • 跨平台兼容性:ctrl_c()是Tokio提供的跨平台函数,专门处理终端Ctrl+C输入;Unix信号仅在类Unix系统生效,Windows下会自动忽略相关监听,不会报错。
  • 统一停机逻辑:所有触发信号都会进入同一个停机流程,避免重复代码。
  • select!宏:用于同时等待多个异步任务,任意一个任务完成就会继续执行后续逻辑,完美适配多信号监听场景。

内容的提问来源于stack exchange,提问作者Marvin.Hansen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 08:30:24