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

如何配置actix-web应用优雅关闭持久化连接?

在Actix-web中实现持久化连接的优雅关闭

可以通过自定义中间件、信号处理结合服务器配置,实现更优雅的持久化连接关闭逻辑,具体方案如下:

1. 给响应添加Connection: close头告知客户端

在应用进入关闭阶段时,给所有后续响应添加该头,让客户端知道无需复用当前连接。实现方式是通过共享状态标记关闭状态,再用中间件拦截响应添加头:

use actix_web::{dev::{Service, ServiceRequest, ServiceResponse, Transform}, Error, HttpResponse};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use futures::future::{ok, Ready};

// 共享的关闭状态标记
#[derive(Clone)]
struct ShutdownState(Arc<AtomicBool>);

// 自定义中间件:在关闭阶段添加Connection: close头
pub struct CloseConnectionMiddleware {
    state: ShutdownState,
}

impl CloseConnectionMiddleware {
    pub fn new(state: ShutdownState) -> Self {
        CloseConnectionMiddleware { state }
    }
}

impl<S, B> Transform<S, ServiceRequest> for CloseConnectionMiddleware
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = CloseConnectionMiddlewareService<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(CloseConnectionMiddlewareService {
            service,
            state: self.state.clone(),
        })
    }
}

pub struct CloseConnectionMiddlewareService<S> {
    service: S,
    state: ShutdownState,
}

impl<S, B> Service<ServiceRequest> for CloseConnectionMiddlewareService<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = futures::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;

    fn poll_ready(&self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let state = self.state.clone();
        let fut = self.service.call(req);

        Box::pin(async move {
            let mut res = fut.await?;
            if state.0.load(Ordering::SeqCst) {
                res.response_mut().headers_mut().insert(
                    actix_web::http::header::CONNECTION,
                    actix_web::http::header::HeaderValue::from_static("close"),
                );
            }
            Ok(res)
        })
    }
}

2. 结合信号处理与优雅关闭配置

在启动服务器时,监听SIGTERM信号,标记关闭状态,并配置服务器的优雅关闭超时,确保现有请求处理完成,同时让中间件生效:

use actix_web::{web, App, HttpServer};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let shutdown_state = Arc::new(AtomicBool::new(false));
    let state_clone = shutdown_state.clone();

    // 监听SIGTERM信号,标记应用进入关闭状态
    tokio::spawn(async move {
        let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
            .expect("Failed to install SIGTERM handler");
        sigterm.recv().await;
        state_clone.store(true, Ordering::SeqCst);
    });

    let server = HttpServer::new(move || {
        App::new()
            .wrap(CloseConnectionMiddleware::new(ShutdownState(shutdown_state.clone())))
            .keep_alive(Duration::from_secs(60)) // 正常状态下的空闲连接超时
            .route("/", web::get().to(|| async { "Hello World!" }))
    })
    .bind(("127.0.0.1", 8080))?;

    // 启动优雅关闭:等待30秒处理现有请求,之后强制关闭
    server.graceful_shutdown(Duration::from_secs(30)).await?;

    Ok(())
}

3. 主动关闭空闲连接(可选)

如果需要更主动地断开空闲连接,可以结合连接状态跟踪,在进入关闭阶段后缩短空闲超时时间,或者通过tokio任务主动关闭空闲的连接实例。不过上述方案已经能覆盖大部分场景,让客户端主动关闭连接,避免后续请求失败。

通过这套组合方案,应用收到SIGTERM后会:

  • 停止接收新连接
  • 给所有新响应添加Connection: close头,告知客户端不再复用连接
  • 等待现有请求处理完成,超时后强制关闭
  • 客户端收到响应头后会主动关闭连接,不会在该连接上发起新请求

内容的提问来源于stack exchange,提问作者Dreamer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 21:45:46