好的,好的.

我有一个可以工作的微服务,它通过Tokio/Quonent提供GRPC,并通过WARP公开指标.当接收到SIGINT信号时,http和GRPC服务都会正确关闭,即通过终端终止.

满分code on Github,相关代码如下:

    // 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,也不会响应其他信号,例如sig-Quit,因为它只有一个用于sigint的信号处理程序.因为该服务应该在多个平台上运行,所以没有给出sig-int是唯一要处理的信号.

我做了一些在线搜索,但不知何故,大多数例子都与我正在使用的相似,所以我不太清楚如何处理Tokio中的多个信号,以便在任何信号触发时触发正常关机.

因此,我的问题是,如何修改信号处理程序,使其捕获多个关闭信号,如Sigint、Ctrr-c或sig-Quit?

先谢谢你了.

推荐答案

您声称监听SIGINT(=Ctrl+C),但SignalType::terminate()实际上监听SIGTERM(=kill或系统关闭).

我不确定是否真的有必要听SIGQUIT;在大多数情况下,SIGINTSIGTERM就足够了.

当然,你可以听两个,通过tokio::select!(). 这里有一个例子.代码取自tokio-graceful-shutdown.(免责声明:我是这个箱子的作者)

/// Waits for a signal that requests a graceful shutdown, like SIGTERM or SIGINT.
#[cfg(unix)]
async fn wait_for_signal_impl() {
    use tokio::signal::unix::{signal, SignalKind};

    // Infos here:
    // https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html
    let mut signal_terminate = signal(SignalKind::terminate()).unwrap();
    let mut signal_interrupt = signal(SignalKind::interrupt()).unwrap();

    tokio::select! {
        _ = signal_terminate.recv() => tracing::debug!("Received SIGTERM."),
        _ = signal_interrupt.recv() => tracing::debug!("Received SIGINT."),
    };
}

/// Waits for a signal that requests a graceful shutdown, Ctrl-C (SIGINT).
#[cfg(windows)]
async fn wait_for_signal_impl() {
    use tokio::signal::windows;

    // Infos here:
    // https://learn.microsoft.com/en-us/windows/console/handlerroutine
    let mut signal_c = windows::ctrl_c().unwrap();
    let mut signal_break = windows::ctrl_break().unwrap();
    let mut signal_close = windows::ctrl_close().unwrap();
    let mut signal_shutdown = windows::ctrl_shutdown().unwrap();

    tokio::select! {
        _ = signal_c.recv() => tracing::debug!("Received CTRL_C."),
        _ = signal_break.recv() => tracing::debug!("Received CTRL_BREAK."),
        _ = signal_close.recv() => tracing::debug!("Received CTRL_CLOSE."),
        _ = signal_shutdown.recv() => tracing::debug!("Received CTRL_SHUTDOWN."),
    };
}

/// Registers signal handlers and waits for a signal that
/// indicates a shutdown request.
pub(crate) async fn wait_for_signal() {
    wait_for_signal_impl().await
}

Rust相关问答推荐

如何处理对打包字段的引用是未对齐错误?

如何在Rust中为具有多个数据持有者的enum变体编写文档 comments ?

Rust TcpStream不能在读取后写入,但可以在不读取的情况下写入.为什么?

S在Cargo.toml中添加工作空间开发依赖关系的正确方法是什么?

在我的Cargo 中,当我在建筑物中使用时,找不到我可以在产品包中使用的 crate .r我如何解决这个问题?

铁 rust ,我的模块介绍突然遇到了一个问题

变量需要parse()中的显式类型

在macro_rule中拆分模块和函数名

Rust 中的复合 `HashSet` 操作或如何在 Rust 中获得 `HashSet` 的显式差异/并集

一次不能多次borrow *obj作为可变对象

Rust 中的内存管理

Option<&T> 如何实现复制

为什么不能在 Rust 中声明静态或常量 std::path::Path 对象?

切片不能被 `usize` 索引?

SDL2 没有在终端键上触发?

试图理解 Rust 中的可变闭包

如何在 Rust 中编写修改 struct 的函数

在空表达式语句中移动的值

Cargo:如何将整个目录或文件包含在功能标志中?

以下打印数组每个元素的 Rust 代码有什么问题?