在使用axum写接口handle的时候,有时候会遇到接口处理资源消耗多、时间长的情况,由于时间比较长,客户端有可能会断开连接,同时axum后端也会在适合的时候中断handle的执行,本文研究了中断的时机。


如下代码写了一个示例,其中sleep是使用的同步的,为了展示其无法中断的特性。

async fn handle() -> (StatusCode, String) {
    println!("start request");
    for i in 0..5 {
        println!("rank {}", i);
        std::thread::sleep(std::time::Duration::from_secs(1));
    }
    println!("end request");
    (StatusCode::OK, "Hello, World!".into())
}

这时候客户端请求并立马中断,服务端打印:

start request
rank 0
rank 1
rank 2
rank 3
rank 4
end request

可以看到代码没有中断执行

修改代码如下,将同步sleep改为异步:

async fn handle() -> (StatusCode, String) {
    println!("start request");
    for i in 0..5 {
        println!("rank {}", i);
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    }
    println!("end request");
    (StatusCode::OK, "Hello, World!".into())
}

这时候再次请求客户端并立马中断,服务端打印:

start request
rank 0

可以看到这次代码中断了,这个所谓的中断时机就是awaitaxum会在每次控制权交回前,检查连接是否中断,如果中断,则不再执行后续代码


从上面的例子可得,如果handle中存在一些耗时耗资源的操作,想要手动控制客户端断开时的中断代码,可以通过交出异步控制权去实现中断代码,如下:

async fn handle() -> (StatusCode, String) {
    func1(); // 耗时操作,客户端可能断开连接

    tokio::task::yield_now().await; // 交出异步控制权,不再执行后面的代码

    func2(); // 耗资源操作,当客户端断开连接时,不再执行这一端
    (StatusCode::OK, "Hello, World!".into())
}