我正在开发一个基于 Web 的应用程序,旨在在嵌入式 Linux 设备中运行。设备启动时我需要启动两个部分:节点服务器(本地服务)和齿轮网络浏览器指向服务器 url。由于节点需要一段时间来启动和接受连接(大约8秒),我需要延迟网络浏览器的启动,直到它准备好。
我决定使用systemddbus服务类型为了实现这一点,我们的想法是在节点服务器完全初始化时获取 dbus 名称,以便我们可以尽快启动 Web 浏览器。我已经在 dbus 中使用名称注册了节点服务器实现server.node
,然后以这种方式配置节点服务(server-node.service):
[Unit]
Description=starts node backend
[Service]
BusName=server.node
EnvironmentFile=/etc/server/server-node.conf
ExecStart=/usr/bin/node /opt/server/main.js 2>&1 | logger
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -2 $MAINPID
KillMode=process
Restart=on-failure
RestartSec=10s
[Install]
WantedBy=multi-user.target
这是网络浏览器的服务(browser.service):
[Unit]
Description=starts cog web browser
Requires=server-node.service
After=server-node.sevice
[Service]
Type=simple
EnvironmentFile=/etc/server/cog.conf
#ExecStartPre=/bin/sleep 20
ExecStart=/usr/bin/cog http://localhost:3000/ui
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -2 $MAINPID
KillMode=process
Restart=on-failure
RestartSec=2s
[Install]
WantedBy=graphical-session.target
这里的问题是无法实现浏览器服务等待节点服务完全激活。当我执行systemctl start server-node.service
命令时需要一段时间(这意味着 systemd 正在等待它在 dbus 中注册)并且运行systemctl status server-node.service
显示它处于activating
状态,之后它就进入了active
。
但是,如果我停止它并运行systemctl start browser.service
,两个服务都会启动,但是浏览器从一开始就运行,显示 HTTP 错误,因为服务器尚无法访问。当我取消注释该ExecStartPre=
行时,它会在启动浏览器之前休眠 20 秒,一切都会正常运行,但我不想使用任何硬编码的延迟。
我也尝试过BindsTo=server-node.service
指示,但同样的情况也会发生。
更新
这是我的main.ts
,我在其中初始化服务器并使用NestJs事件框架:
async function bootstrap() {
// Prepare the environment
// .........
await app.listen(port, 'localhost').then(() => {
if (env === 'DEV') {
Logger.log(`| ${env} server listening on: http://localhost:${port} `);
} else {
Logger.log(`| ${env} server listening on port ${port} `);
}
// Grab the execution context to emit the app initialized event
// https://stackoverflow.com/a/53484892/1199132
app.get(EventEmitter2).emit('app.initialized');
});
}
bootstrap();
然后在dbus.service.ts
事件app.initialized
被捕获并处理时,在dbus中注册名称,使用dbus 原生库:
@OnEvent('app.initialized', { async: true })
register () {
Logger.log('Requesting dbus name ' + this.busName, this.constructor.name);
// Request the bus name
// See https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-messages
this.bus.requestName(this.busName, 0, (err, name) => {
if (err) {
Logger.error('Error registering dbus name: ' + err, DbusService.name);
} else {
Logger.log('Registered dbus name with response: ' + name, DbusService.name);
}
});
}
dbus 名称已列出当使用显示系统 dbus 名称时busctl list
。
答案1
也许这就是您所需要的,如下:
对于在 DBus 系统总线上获取名称的服务,请使用 Type=dbus 并相应地设置 BusName=。该服务不应分叉(守护进程)。一旦在系统总线上获取了名称,systemd 将认为该服务已初始化。以下示例显示了典型的 DBus 服务:
[Unit]
Description=Simple DBus service
[Service]
Type=dbus
BusName=org.example.simple-dbus-service
ExecStart=/usr/sbin/simple-dbus-service
[Install]
WantedBy=multi-user.target
对于总线可激活服务,不要在 systemd 服务文件中包含 [Install] 部分,而是在相应的 DBus 服务文件中使用 SystemdService= 选项,例如 (/usr/share/dbus-1/system-services/org .example.simple-dbus-service.service):
[D-BUS Service]
Name=org.example.simple-dbus-service
Exec=/usr/sbin/simple-dbus-service
User=root
SystemdService=simple-dbus-service.service
答案2
ExecStart=/usr/bin/node /opt/server/main.js 2>&1 |记录器
您不能在这里使用 shell 语法,例如。该2>&1 | logger
部分是不允许的。
(实际上不需要,因为 stdout 和 stderr 被捕获并可以使用systemctl status
和/或journalctl
无论如何查看......)
这里的问题是我无法实现浏览器服务来等待节点服务完全激活。
为此,我建议您在准备好接受连接时创建一个“信号量文件”,比如说/tmp/node.server.ready
(只是一个空的常规文件)。然后,当使用该指令创建文件时,使用该指令
创建一个*.path
启动浏览器单元的单元。 看:Unit=browser.service
PathExists=/tmp/node.server.ready
man systemd.path
- ⚠️确保在启动节点服务器之前删除该
/tmp/node.server.ready
文件,并在节点服务器停止时再次删除...
这样你应该仅启用并使其拉入路径单元,当它准备好时,server-node.service
路径单元又会激活......browser.service