rust 如何正常关闭时雄运行时以响应SIGTERM?

pinkon5k  于 2023-03-18  发布在  其他
关注(0)|答案(2)|浏览(216)

我有一个main函数,其中创建了一个时雄运行时并在其上运行两个future。

use tokio;

fn main() {
    let mut runtime = tokio::runtime::Runtime::new().unwrap();

    runtime.spawn(MyMegaFutureNumberOne {});
    runtime.spawn(MyMegaFutureNumberTwo {});

    // Some code to 'join' them after receiving an OS signal
}

如何接收SIGTERM,等待所有未完成的任务(NotReady)并退出应用程序?

ccgok5k5

ccgok5k51#

处理信号是很棘手的,要解释如何处理所有可能的情况太宽泛了。信号的实现不是跨平台的标准,所以我的答案是针对Linux的。如果你想更跨平台,使用POSIX函数sigaction结合pause;这会给你更多的控制权。
时雄的文档中有一个很好的getting started guide用于tokio中的信号。因此,我将尝试添加我自己的建议。
我的一般建议是有一个任务,将处理信号为我们,然后您使用一个手表通道在您的其他任务,将停止,如果手表通道状态发生变化。
我的第二个建议是在select上使用biased,因为select会等待你的将来,这是很重要的,因为你通常想知道一个信号是否已经被立即接收到,而不是在之前做其他事情。这可能是一个经常准备就绪的忙碌循环的问题,你永远不会得到你的信号将来分支。请仔细阅读关于biased的文档。

use core::time::Duration;

use tokio::{
    select,
    signal::unix::{signal, SignalKind},
    sync::watch,
    time::sleep,
};

#[tokio::main]
async fn main() {
    let (stop_tx, mut stop_rx) = watch::channel(());

    tokio::spawn(async move {
        let mut sigterm = signal(SignalKind::terminate()).unwrap();
        let mut sigint = signal(SignalKind::interrupt()).unwrap();
        loop {
            select! {
                _ = sigterm.recv() => println!("Recieve SIGTERM"),
                _ = sigint.recv() => println!("Recieve SIGTERM"),
            };
            stop_tx.send(()).unwrap();
        }
    });

    loop {
        select! {
            biased;

            _ = stop_rx.changed() => break,
            i = some_operation(42) => {
                println!("Result is {i}");
                unsafe { libc::raise(libc::SIGTERM)};
            },
        }
    }
}

async fn some_operation(i: u64) -> u64 {
    println!("Task started.");
    sleep(Duration::from_millis(i)).await;
    println!("Task shutting down.");
    i
}

您可以克隆的接收器的通道需要,这将使有效地处理信号。
时雄0.1
实现您所需的一种方法是使用tokio_signal crate捕获信号,如下所示:(doc example)

extern crate futures;
extern crate tokio;
extern crate tokio_signal;

use futures::prelude::*;
use futures::Stream;
use std::time::{Duration, Instant};
use tokio_signal::unix::{Signal, SIGINT, SIGTERM};

fn main() -> Result<(), Box<::std::error::Error>> {
    let mut runtime = tokio::runtime::Runtime::new()?;

    let sigint = Signal::new(SIGINT).flatten_stream();
    let sigterm = Signal::new(SIGTERM).flatten_stream();

    let stream = sigint.select(sigterm);

    let deadline = tokio::timer::Delay::new(Instant::now() + Duration::from_secs(5))
        .map(|()| println!("5 seconds are over"))
        .map_err(|e| eprintln!("Failed to wait: {}", e));

    runtime.spawn(deadline);

    let (item, _rest) = runtime
        .block_on_all(stream.into_future())
        .map_err(|_| "failed to wait for signals")?;

    let item = item.ok_or("received no signal")?;
    if item == SIGINT {
        println!("received SIGINT");
    } else {
        assert_eq!(item, SIGTERM);
        println!("received SIGTERM");
    }

    Ok(())
}

此程序将等待所有当前任务完成,并将捕获选定的信号。这似乎在Windows上不起作用,因为它会立即关闭程序。

vjhs03f7

vjhs03f72#

对于时雄版本1.x.y,Tokio官方教程有一个关于此主题的页面:Graceful shutdown

相关问题