我想将 Java 应用程序实现为守护进程/服务,并在使用标准 Debian Stretch(内核版本 4.9)的 Raspberry Pi 上运行。
Java 应用程序启动后会抛出异常,因为它无法读取位于 jar 之外的重要配置文件。这是设计使然。我想将配置文件保留在 jar 之外。
我通过将配置文件放入 jar 并通过 InputStream 读取文件来使其运行。但要求配置文件不在 jar 内。通过终端手动启动 jar 也可以。文件权限应该没问题,正如以 root 身份手动启动所表明的那样。
我的直觉是,服务启动时工作目录会变得混乱。另一个直觉是,通过 FileInputStream 浏览文件系统会导致问题。
这是我的服务文件:
[Unit]
Description=collector
[Service]
User=root
CHDIR=/opt/servicedir/
#application.properties:
ExecStart=/usr/bin/java -jar /opt/servicedir/javaapp.jar
SuccessExitStatus=143
TimeoutStopSec=10
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
/opt/servicedir/ 中所有内容的权限:sudo chmod -R 770 /opt/servicedir/
来自系统日志的异常:
Aug 31 14:38:58 raspberrypi systemd[1]: myservice.service: Main process exited, code=exited, status=1/FAILURE
Aug 31 14:38:58 raspberrypi systemd[1]: myservice.service: Unit entered failed state.
Aug 31 14:38:58 raspberrypi systemd[1]: myservice.service: Failed with result 'exit-code'.
Aug 31 14:39:03 raspberrypi systemd[1]: myservice.service: Service hold-off time over, scheduling restart.
Aug 31 14:39:03 raspberrypi systemd[1]: Stopped service
Aug 31 14:39:03 raspberrypi systemd[1]: Started service
Aug 31 14:39:04 raspberrypi java[7982]: Exception in thread "main" java.lang.ExceptionInInitializerError
Aug 31 14:39:04 raspberrypi java[7982]: #011at foo.bar.Application.<clinit>(Application.java:20)
Aug 31 14:39:04 raspberrypi java[7982]: #011at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Aug 31 14:39:04 raspberrypi java[7982]: #011at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
Aug 31 14:39:04 raspberrypi java[7982]: #011at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Aug 31 14:39:04 raspberrypi java[7982]: #011at java.lang.reflect.Method.invoke(Method.java:497)
Aug 31 14:39:04 raspberrypi java[7982]: #011at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
Aug 31 14:39:04 raspberrypi java[7982]: #011at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
Aug 31 14:39:04 raspberrypi java[7982]: #011at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
Aug 31 14:39:04 raspberrypi java[7982]: #011at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Aug 31 14:39:04 raspberrypi java[7982]: Caused by: java.lang.RuntimeException: Failed to load credentials.properties.
Aug 31 14:39:04 raspberrypi java[7982]: #011at foo.bar.Config.<clinit>(Config.java:60)
Aug 31 14:39:04 raspberrypi java[7982]: #011... 9 more
Aug 31 14:39:04 raspberrypi java[7982]: Caused by: java.io.FileNotFoundException: ./res/credentials.properties (Datei oder Verzeichnis nicht gefunden)
Aug 31 14:39:04 raspberrypi java[7982]: #011at java.io.FileInputStream.open0(Native Method)
Aug 31 14:39:04 raspberrypi java[7982]: #011at java.io.FileInputStream.open(FileInputStream.java:195)
Aug 31 14:39:04 raspberrypi java[7982]: #011at java.io.FileInputStream.<init>(FileInputStream.java:138)
Aug 31 14:39:04 raspberrypi java[7982]: #011at java.io.FileInputStream.<init>(FileInputStream.java:93)
Aug 31 14:39:04 raspberrypi java[7982]: #011at foo.bar.Application.Config.<clinit>(Config.java:44)
Aug 31 14:39:04 raspberrypi java[7982]: #011... 9 more
Aug 31 14:39:04 raspberrypi systemd[1]: myservice.service: Main process exited, code=exited, status=1/FAILURE
Aug 31 14:39:04 raspberrypi systemd[1]: myservice.service: Unit entered failed state.
Aug 31 14:39:04 raspberrypi systemd[1]: myservice.service: Failed with result 'exit-code'.
以下是来自 Java 代码的代码行:
// this does not work :
String credentialsFilePath = "./res/credentials.properties"
try (FileInputStream in = new FileInputStream(credentialsFilePath)) {
// this line works with the config file inside the jar
String credentialsInJar = "credentials.properties"
try (InputStream in = Config.class.getResourceAsStream(credentialsInJar)) {
答案1
您尚未在 systemd 单元中设置工作目录。看起来您故意的但要设置一个。
我懂了:
CHDIR=/opt/servicedir/
但没有这样的配置选项。
要设置服务启动的工作目录,请使用WorkingDirectory=
。 例如:
WorkingDirectory=/opt/servicedir