我需要在外部机器收到 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
(和network-online.target
)已启动,但我试图在 Node 脚本中声明的 IP 尚未分配,那么我可能需要等待一个服务来保证 IP 地址已完全配置。这可能是问题所在吗?如果是,是否有合适的服务可以依赖?
答案1
这可能是基于套接字的操作的一个有趣用途。它不是在启动时启动您的服务,而是在第一次收到网络请求时启动以激活它。到那时,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 套接字激活] 的帖子。
答案2
我将服务器监听的 IP 地址从问题日志中的特定 IP 地址更改为通配符。0.0.0.0
这似乎解决了问题(这表明我对未分配 DHCP 地址的怀疑可能是正确的)。
这不是一个完美的解决方案(因为我仍然不知道如何等待 DHCP 分配的地址可用),但由于它对这种特殊情况没有任何实际区别,它解决了记录错误的直接问题。