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

如何在应用生命周期非启动阶段启动actix-web服务器?

解决Windows服务中Actix-Web服务器的启停问题

核心结论

  • 不需要在main()上添加#[actix_web::main]:Windows服务的main()是同步入口,我们需要手动管理Actix的运行时(Runtime),无需依赖该宏自动初始化。
  • 启动服务器的函数也无需加该宏:通过手动创建Actix Runtime来运行异步的服务器启动逻辑,同时保留服务器句柄用于后续启停操作即可。

实现思路

  1. 手动管理Actix Runtime:#[actix_web::main]的本质是帮你自动初始化Actix Runtime并运行异步main函数。在Windows服务场景下,我们需要自行创建actix_rt::Runtime,在服务初始化完成后启动服务器。
  2. 保存服务器句柄:HttpServer::run()返回的Server对象可通过.handle()获取ServerHandle,保存这个句柄就能在需要时调用.stop()终止服务器。
  3. 对接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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 10:20:38