如何在应用生命周期非启动阶段启动actix-web服务器?
解决Windows服务中Actix-Web服务器的启停问题
核心结论
- 不需要在
main()上添加#[actix_web::main]:Windows服务的main()是同步入口,我们需要手动管理Actix的运行时(Runtime),无需依赖该宏自动初始化。 - 启动服务器的函数也无需加该宏:通过手动创建Actix Runtime来运行异步的服务器启动逻辑,同时保留服务器句柄用于后续启停操作即可。
实现思路
- 手动管理Actix Runtime:
#[actix_web::main]的本质是帮你自动初始化Actix Runtime并运行异步main函数。在Windows服务场景下,我们需要自行创建actix_rt::Runtime,在服务初始化完成后启动服务器。 - 保存服务器句柄:
HttpServer::run()返回的Server对象可通过.handle()获取ServerHandle,保存这个句柄就能在需要时调用.stop()终止服务器。 - 对接Windows服务控制逻辑:在服务的控制处理函数中,响应
Stop/Shutdown命令,调用保存的ServerHandle停止服务器。
示例代码
use actix_web::{web, App, HttpServer}; use std::sync::{Arc, Mutex}; use windows_service::{ service::{ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus, ServiceType}, service_control_handler::{self, ServiceControlHandlerResult}, service_dispatcher, }; use once_cell::sync::Lazy; const SERVICE_NAME: &str = "actix_windows_service"; const SERVICE_DISPLAY_NAME: &str = "Actix Web Windows Service"; // 安全存储服务器句柄,用于后续停止操作 static SERVER_HANDLE: Lazy<Arc<Mutex<Option<actix_web::dev::ServerHandle>>>> = Lazy::new(|| Arc::new(Mutex::new(None))); // 服务控制处理函数,响应系统的停止/关机命令 fn service_control_handler(control: ServiceControl) -> ServiceControlHandlerResult { match control { ServiceControl::Stop | ServiceControl::Shutdown => { if let Some(handle) = SERVER_HANDLE.lock().unwrap().take() { handle.stop(true); // 优雅停止服务器,等待现有请求处理完成 } ServiceControlHandlerResult::NoError } _ => ServiceControlHandlerResult::NotImplemented, } } // 异步启动服务器的逻辑 async fn start_server() -> std::io::Result<()> { let server = HttpServer::new(|| { App::new() .route("/", web::get().to(|| async { "Hello from Windows Service!" })) }) .bind(("127.0.0.1", 8080))? .run(); // 保存服务器句柄到全局存储 let handle = server.handle(); SERVER_HANDLE.lock().unwrap().replace(handle); server.await // 阻塞等待服务器停止 } // Windows服务的核心运行逻辑 fn run_service() -> windows_service::Result<()> { let status_handle = service_control_handler::register(SERVICE_NAME, service_control_handler)?; // 更新服务状态为运行中,告知系统服务已启动 status_handle.set_service_status(ServiceStatus { service_type: ServiceType::OWN_PROCESS, current_state: ServiceState::Running, controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SHUTDOWN, exit_code: ServiceExitCode::Win32(0), checkpoint: 0, wait_hint: 0, process_id: None, })?; // 创建Actix Runtime并启动服务器 let rt = actix_rt::Runtime::new()?; match rt.block_on(start_server()) { Ok(_) => { // 服务器正常停止,更新服务状态为已停止 status_handle.set_service_status(ServiceStatus { current_state: ServiceState::Stopped, exit_code: ServiceExitCode::Win32(0), ..ServiceStatus::default() })?; } Err(e) => { // 服务器启动失败,更新服务状态并输出错误 eprintln!("Server failed to run: {}", e); status_handle.set_service_status(ServiceStatus { current_state: ServiceState::Stopped, exit_code: ServiceExitCode::Win32(e.raw_os_error().unwrap_or(1)), ..ServiceStatus::default() })?; } } Ok(()) } // Windows服务的入口函数 fn main() -> windows_service::Result<()> { service_dispatcher::start(SERVICE_NAME, run_service) }
关键细节
- 线程安全的句柄存储:使用
Arc<Mutex<Option<ServerHandle>>>替代不安全的全局变量,确保服务控制线程和服务器线程之间安全共享句柄。 - 服务状态更新:必须正确更新Windows服务的状态,否则系统会判定服务异常。
- 优雅停止逻辑:
ServerHandle::stop(true)会等待现有请求处理完成后关闭服务器;传入false则会强制终止所有连接。
内容的提问来源于stack exchange,提问作者TheFlashes
相关产品推荐
相关产品推荐

