如何配置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
相关产品推荐
相关产品推荐

