Node.js

Node.js 伺服器最初因 systemd EADDRNOTAVAIL 而失敗

  • January 23, 2017

我需要在來自外部機器的 http get 請求上啟動一個腳本,為此我正在使用 Node.js。我希望伺服器以系統啟動。一切正常,只是伺服器在啟動過程中啟動得太早了:

-- Logs begin at Mon 2017-01-23 09:55:21 UTC, end at Mon 2017-01-23 10:36:20 UTC. --
Jan 23 09:55:24 powercontrol systemd[1]: Starting Energenie Listener...
Jan 23 09:55:24 powercontrol systemd[1]: Started Energenie Listener.
Jan 23 09:55:28 powercontrol node[474]: events.js:160
Jan 23 09:55:28 powercontrol node[474]: throw er; // Unhandled 'error' event
Jan 23 09:55:28 powercontrol node[474]: ^
Jan 23 09:55:28 powercontrol node[474]: Error: listen EADDRNOTAVAIL 192.168.40.62:8001
Jan 23 09:55:28 powercontrol node[474]: at Object.exports._errnoException (util.js:1022:11)
Jan 23 09:55:28 powercontrol node[474]: at exports._exceptionWithHostPort (util.js:1045:20)
Jan 23 09:55:28 powercontrol node[474]: at Server._listen2 (net.js:1249:19)
Jan 23 09:55:28 powercontrol node[474]: at listen (net.js:1298:10)
Jan 23 09:55:28 powercontrol node[474]: at doListening (net.js:1397:7)
Jan 23 09:55:28 powercontrol node[474]: at _combinedTickCallback (internal/process/next_tick.js:77:11)
Jan 23 09:55:28 powercontrol node[474]: at process._tickCallback (internal/process/next_tick.js:98:9)
Jan 23 09:55:28 powercontrol node[474]: at Module.runMain (module.js:607:11)
Jan 23 09:55:28 powercontrol node[474]: at run (bootstrap_node.js:420:7)
Jan 23 09:55:28 powercontrol node[474]: at startup (bootstrap_node.js:139:9)
Jan 23 09:55:28 powercontrol systemd[1]: energenie_listener.service: main process exited, code=exited, s
Jan 23 09:55:28 powercontrol systemd[1]: Unit energenie_listener.service entered failed state.
Jan 23 09:55:30 powercontrol systemd[1]: energenie_listener.service holdoff time over, scheduling restar
Jan 23 09:55:30 powercontrol systemd[1]: Stopping Energenie Listener...
Jan 23 09:55:30 powercontrol systemd[1]: Starting Energenie Listener...
Jan 23 09:55:30 powercontrol systemd[1]: Started Energenie Listener.
Jan 23 09:55:31 powercontrol node[565]: events.js:160
Jan 23 09:55:31 powercontrol node[565]: throw er; // Unhandled 'error' event
Jan 23 09:55:31 powercontrol node[565]: ^
Jan 23 09:55:31 powercontrol node[565]: Error: listen EADDRNOTAVAIL 192.168.40.62:8001
Jan 23 09:55:31 powercontrol node[565]: at Object.exports._errnoException (util.js:1022:11)
Jan 23 09:55:31 powercontrol node[565]: at exports._exceptionWithHostPort (util.js:1045:20)
Jan 23 09:55:31 powercontrol node[565]: at Server._listen2 (net.js:1249:19)
Jan 23 09:55:31 powercontrol node[565]: at listen (net.js:1298:10)
Jan 23 09:55:31 powercontrol node[565]: at doListening (net.js:1397:7)
Jan 23 09:55:31 powercontrol node[565]: at _combinedTickCallback (internal/process/next_tick.js:77:11)
Jan 23 09:55:31 powercontrol node[565]: at process._tickCallback (internal/process/next_tick.js:98:9)
Jan 23 09:55:31 powercontrol node[565]: at Module.runMain (module.js:607:11)
Jan 23 09:55:31 powercontrol node[565]: at run (bootstrap_node.js:420:7)
Jan 23 09:55:31 powercontrol node[565]: at startup (bootstrap_node.js:139:9)
Jan 23 09:55:31 powercontrol systemd[1]: energenie_listener.service: main process exited, code=exited, s
Jan 23 09:55:31 powercontrol systemd[1]: Unit energenie_listener.service entered failed state.
Jan 23 09:55:33 powercontrol systemd[1]: energenie_listener.service holdoff time over, scheduling restar
Jan 23 09:55:33 powercontrol systemd[1]: Stopping Energenie Listener...
Jan 23 09:55:33 powercontrol systemd[1]: Starting Energenie Listener...
Jan 23 09:55:33 powercontrol systemd[1]: Started Energenie Listener.
Jan 23 09:55:34 powercontrol node[572]: Server running at http://192.168.40.62:8001/

正如你所看到的,在幾次失敗之後,伺服器最終啟動並且之後它工作正常。該.service文件如下:

[Unit]
Description=Energenie Listener
After=network.target systemd-journald.service

[Service]
ExecStart=/usr/bin/node /var/opt/energenie/energenie_listener.js
Restart=always
RestartSec=2
User=root
Group=root
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/var/opt/energenie

[Install]
WantedBy=multi-user.target

以及After=network.target ...,我試過network-online.target了,但它沒有任何區別。

我應該等待不同的服務嗎?哪一個?

我確實有一個想法:這台機器有一個靜態 IP,但它是通過 DHCP 分配的。如果問題是network.target(and network-online.target) 已啟動,但我試圖在 Node 腳本中聲明的 IP 尚未分配,大概我需要一個服務來等待,這將保證 IP 地址已完全配置。這可能是問題所在嗎?如果是這樣,是否有合適的服務可供依賴?

我將伺服器正在偵聽的 IP 地址從問題日誌中的特定地址更改為萬用字元0.0.0.0. 這似乎解決了問題(這表明我對未分配 DHCP 地址的懷疑可能是正確的)。

這不是一個完美的解決方案(因為我仍然不知道如何等待 DHCP 分配的地址可用),但由於它對於這種特殊情況沒有任何實際區別,它解決了直接的問題記錄的錯誤。

對於基於套接字的操作,這可能是一個有趣的用途。它不會在啟動時啟動您的服務,而是會在第一次網路請求進入以啟動它時啟動。到時候DHCP網路分配應該就完成了!

在您的服務單元文件中,您向該部分添加條目以[Service]聲明 STDIN 的來源:

StandardInput=socket

然後創建一個.socket與您的文件同名的.service文件,如下所示:

[Unit]
Description=Energenie Listener Socket

[Socket]
# Depending your app, you might also use ListenStream= 
# or ListenSequentialPacket= See man systemd.socket for details
ListenDatagram=192.168.40.62:8001
# Allow binding to addresses that may not be configured yet.
FreeBind=true

[Install]
WantedBy=sockets.target

確保systemd enable在重新啟動之前在套接字上執行。

有關更多上下文,請參閱man systemd.socket或搜尋關於

$$ systemd socket activation $$.

引用自:https://serverfault.com/questions/827986