我有一個服務 X,需要在啟動時連接到偵聽套接字。這個目標套接字本身是由 systemd 啟動的另一個服務 Y 開啟的。
有沒有辦法在單元檔案(或其他方式)中指定僅在服務 Y 成功啟動並開啟偵聽套接字後才啟動服務 X?
請注意,如果初始連線失敗,我無法變更服務 X 以重試。此外,固定延遲也無法很好地工作,因為服務 Y 在打開偵聽套接字之前需要不同的時間。
答案1
systemd 的運作方式往往略有不同。您將 systemd 配置為建立並偵聽套接字,如果像 X 這樣的任何人嘗試連接,則 systemd 會啟動 Y 來處理連接,並將套接字傳遞給它。所以名義上 X 可以在 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 文件應該會有所幫助。