我有一个服务 X,需要在启动时连接到侦听套接字。该目标套接字本身是由 systemd 启动的另一个服务 Y 打开的。
有没有办法在单元文件(或其他方式)中指定仅在服务 Y 成功启动并打开侦听套接字后才启动服务 X?
请注意,如果初始连接失败,我无法更改服务 X 以重试。此外,固定延迟也不能很好地工作,因为服务 Y 在打开侦听套接字之前需要不同的时间。
答案1
systemd 的工作方式往往略有不同。您将 systemd 配置为创建并侦听套接字,如果像 X 这样的任何人尝试连接,则 systemd 会启动 Y 来处理连接,并将套接字传递给它。因此,名义上 X 可以在 Y 之前开始,但这并不重要。稍后的连接将由 Y 处理。(您也可以对其进行配置,以便为每个连接重新启动 Y,但我认为这不是您的情况)。
对 Y 的最小更改是让它接受预先创建的套接字作为其 stdin/stdout 文件描述符,而不是自行创建/绑定。
这是一个测试设置,您可以尝试,而不是以 root 身份尝试。您需要 3 个单元文件。
~/.local/share/systemd/user/mysock.socket
告诉 systemd 创建套接字以及如何传递它:
# create listening socket. on 1st connect run mysock.service
[Socket]
ListenStream=5555
Accept=false
~/.local/share/systemd/user/mysock.service
(具有相同的名称mysock
)是任何人连接到套接字时将启动的服务。这是你开始 Y 的地方,我已经用一些 python 替换了它。
[Unit]
Description=started from mysock.socket
[Service]
ExecStart=/home/meuh/bin/python/mysock.py
StandardInput=socket
最后,您的 X 服务有一个单元表示它需要套接字。对于 XI,我使用 socat 将日期写入套接字。
~/.local/share/systemd/user/mysockdepend.service
[Unit]
Description=dependent on socket listening
Requires=mysock.socket
[Service]
ExecStart=/usr/bin/socat -U tcp:localhost:5555 exec:/usr/bin/date
python 获取标准输入上的套接字,即文件描述符 0,并将其包装到合适的 python 套接字对象中,执行操作accept()
并可以读取/写入它:
~/bin/python/mysock.py
#!/usr/bin/python
# started from /home/meuh/.local/share/systemd/user/mysock.service
# with socket on stdin/stdout
import sys, time, socket, subprocess
def debug(msg):
# time.sleep(3)
subprocess.call(["/usr/bin/logger", msg])
debug("start\n")
s = socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM)
while True:
conn, addr = s.accept()
conn.send("hello\n")
while True:
try:
data = conn.recv(100)
debug("got "+data+"\n")
if len(data)<=0: break
except:
break
conn.close()
之后systemctl --user daemon-reload
,您应该能够运行 X
systemctl --user start mysockdepend
并在日志中看到journalctl
Y 已启动,以及带有日期的调试输出。
答案2
systemd 处理这种情况套接字文件。
将创建一个名为 systemd 的单元文件some-socket.socket
来表示套接字。
然后您的Service X
服务文件可以包含After=
引用套接字的指令。
有关套接字文件的官方 systemd 文档应该会有所帮助。