我正在尝试使用 windows-service 来运行 actix Web 应用程序。它提供了一个很好的 API 并且大部分都可以工作。我可以很好地开始我的服务。但是,当我尝试停止服务时,出现以下错误:
Error 109: The pipe has been ended
(但是它确实停止了服务)。
我主要只是使用为
windows-service
提供的示例,但这里是相关代码(有关上下文和所有包装函数,请查看 https://github.com/mulvad/windows-service-rs/blob /master/examples/ping_service.rs):
pub fn run_service() -> Result<()> {
fn hi() -> impl actix_web::Responder {
"Hello!\r\n"
}
let sys = actix_rt::System::new("test");
actix_web::HttpServer::new(move || {
actix_web::App::new()
.route("/", actix_web::web::get().to(hi))
})
.bind("0.0.0.0:3000").unwrap()
.start();
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
ServiceControl::Stop => {
actix_rt::System::with_current(|s| s.stop());
ServiceControlHandlerResult::NoError
}
_ => ServiceControlHandlerResult::NotImplemented,
}
};
let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
})?;
sys.run().unwrap();
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
})?;
Ok(())
}
如果我将
System::stop
放入 thread::spawn
中,则会收到不同的错误:The service did not return an error. This could be an internal Windows error or an internal service error
。在这种情况下,它不会停止服务。
我已经添加了一些日志记录,看起来代码从未超过
sys.run().unwrap()
,这很奇怪。
有什么想法吗?我以前从未使用过 Windows Service API,所以我真的不知道我在做什么。
编辑
我弄清楚了主要问题是什么:我必须在停止服务之前通知Windows服务已停止。我整理了一个笨重的方法来让它发挥作用:
std::thread::spawn(move || {
loop {
if shutdown_signal.load(Ordering::Relaxed) {
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
}).unwrap();
actix_rt::System::current().stop();
break;
}
}
});
sys.run().unwrap();
// ...
其中
shutdown_signal
是我在事件处理程序中设置为 true 的 AtomicBool
。我将看看我是否可以通过actix_rt
以某种方式做到这一点。
回答我自己的问题。我认为这是处理它的最佳方法,尽管我很高兴看到其他解决方案!
pub fn run_service() -> Result<()> {
use futures::Future;
fn hi() -> impl actix_web::Responder {
"Hello!\r\n"
}
let sys = actix_rt::System::new("test");
actix_web::HttpServer::new(move || {
actix_web::App::new()
.route("/", actix_web::web::get().to(hi))
})
.bind("0.0.0.0:3000").unwrap()
.start();
let (mut send_stop, recv_stop) = {
let (p, c) = futures::sync::oneshot::channel::<()>();
(Some(p), c)
};
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
ServiceControl::Stop => {
send_stop.take().unwrap().send(()).unwrap();
ServiceControlHandlerResult::NoError
}
_ => ServiceControlHandlerResult::NotImplemented,
}
};
let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
})?;
actix_rt::spawn(recv_stop.map(move |_| {
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
}).unwrap();
actix_rt::System::current().stop()
}).map_err(|_| ()));
sys.run().unwrap();
Ok(())
}