今天是星期五,我想分享一些我今天学到的东西。像很多事情一样,文档是存在的,但将所有东西整合在一起需要一些技巧。因此,希望将来遇到这个问题并寻找答案的人能够从中受益。
我们希望用 Rust 编写的 netavark-dhcp-proxy 应用程序能够以两种模式运行:手动和通过 systemd 套接字激活。手动意味着仅仅作为命令行应用程序运行。通过 systemd 套接字激活基本上意味着 systemd “拥有” 套接字,当有人尝试使用该套接字时,它将启动服务。套接字激活是一种很好的方法,可以使很少运行的服务能够按需可用。如果在服务中内置了非活动超时,那么它可以在满足您想要的任何条件后关闭自身。
关于 systemd 的套接字激活服务需要注意的一点是,当服务启动时,它不会简单地监听特定套接字文件,而是会监听文件描述符 (FD) 3。
“…作为基于套接字的激活逻辑的一部分。它返回接收到的文件描述符的数量。如果没有收到文件描述符,则返回零。第一个文件描述符可以在文件描述符编号 3 处找到…”
sd_listen_fds(3)
要确定您的服务是否是由 systemd 激活的,您可以检查环境变量键 LISTEN_FDS 是否存在。以下是代理代码中来自 Rust 的实际示例,我们在此示例中查找环境变量,如果找到,则从 FD 3 创建一个 UnixListener。
let uds: UnixListener = match env::var("LISTEN_FDS") {
Ok(effds) => {
if effds != "1" {
error!("Received more than one FD from systemd");
return Ok(());
}
let systemd_socket = unsafe { stdUnixListener::from_raw_fd(3) };
UnixListener::from_std(systemd_socket)?
}
// Use the standard socket approach
Err(..) => UnixListener::bind(&uds_path)?,
};
let uds_stream = UnixListenerStream::new(uds);
但是,在测试时,我注意到应用程序退出时的非活动超时没有触发。我获得了应用程序的 PID,strace 显示它正在等待 accept。
$ sudo strace -p 479503 strace: Process 479503 attached accept4(3,
我很困惑。显然是代理处理套接字的方式出了问题,但我无法确定问题所在。然后我的队友 @pholzing 指出 UnixListener::from_std 需要一个非阻塞 FD,而这并不是 from_raw 的默认值。我们只需要将从原始文件描述符打开的套接字更改为非阻塞即可。
let systemd_socket = unsafe { stdUnixListener::from_raw_fd(3) }; systemd_socket.set_nonblocking(true)?;
Bingo!问题解决了。可以在我的 systemd 套接字激活拉取请求 中看到完整的代码。我希望这能帮助将来遇到类似挑战的人。
发表评论