SpringBoot服務無法讀取系統變量,我重新認識了profile和bashrc

背景

CentOS服務器上,我們用Systemd部署了一個SpringBoot服務。關於如何部署,可以參考這篇文章。這個SpringBoot服務會用ProcessBuilder去調用機器上一個C++的可執行文件。

問題描述

SpringBoot程序跑得很正常,但是我們發現C++程序卻沒有log輸出,也就是說它從沒被執行過。
查看了ProcessBuilder的返回值,是127127的意思是系統找不到對應的命令。於是我們把C++程序對應的目錄(裏面包括第三方動態鏈接庫)添加到了LD_LIBRARY_PATH。怎麼添加的呢?我們在~/.bash_profile腳本中添加了命令:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/cppFoler/

經過測試,SpringBoot服務依然無法成功調用C++程序,依然返回127。但奇怪的是,當我們直接使用java -jar命令,並且用nohup的方式令SpringBoot程序跑在後臺,可以正常調用C++程序:

nohup java -jar mySpringBoot.jar

問題分析

這裏就不得不說Linuxnohupservice的區別了。

  • nohup: 在linux中,我們通過終端(shell)登陸機器,會有一個進程與shell關聯。當我們退出的時候(如Ctrl+C),會向該進程發送一個SIGHUP信號,進程收到信號便會自動被銷燬。nohup命令的作用,是將shell中產生的輸出(stdout)和錯誤(stderr)重定向到文件nohup.out中。當我們退出shell的時候,nohup會忽略SIGHUP信號,讓相關聯的程序繼續運行。由此看出,nohup進程是一次性的,其本質就是讓程序在原先的shell進程中繼續運行
  • service: 服務是一種後臺守護進程,不與任何終端(shell)關聯。它是系統的一部分,可以被servicesystemctl命令方便的管理。service的具體的啓動過程可以參考這篇文章

回到我們的問題,nohup的方式可以工作,而service不行,說明前面設置的LD_LIBRARY_PATH參數只對nohup生效,對service無效。這讓我去重新學習了CentOS~/.bash_profile腳本,以及相關的/etc/profile~/.bashrc/etc/bashrc

  • /etc/profile: 腳本設置了一些系統級別的環境變量,它會在有用戶通過終端登陸的時候被執行,做一些初始化操作。初始化時,它會去/etc/profile.d/目錄下,去逐個執行.sh文件。所以,與其直接修改/etc/profile文件,不如將自定義腳本放在/etc/profile.d/下面。
    該腳本只會在**交互式登陸終端(interactive - login shell)**啓動的時候運行,也就是它只在用戶登陸的時候運行一次。以下摘自文件的註釋

System wide environment and startup programs, for login setup. Functions and aliases go in /etc/bashrc. It’s NOT a good idea to change this file unless you know what you are doing. It’s much better to create a custom.sh shell script in /etc/profile.d/ to make custom changes to your environment, as this will prevent the need for merging in future updates.

  • /etc/bashrc: 腳本和/etc/profile類似。區別是,/etc/bashrc更側重的是系統級別的別名(alias)。而且,該腳本只會在**交互式非登陸終端(interactive - non-login shell)**啓動的時候運行,即在每次新打開一個shell的時候,都會執行一遍。以下摘自文件的註釋

System wide functions and aliases, Environment stuff goes in /etc/profile.

  • ~/.bash_profile: 作用和/etc/profile一樣,只不過作用範圍只是當前用戶
  • ~/.bashrc: 作用和/etc/bashrc一樣,只不過作用範圍只是當前用戶

到這裏,問題已經明朗了,我們在~/.bash_profile中設置的環境變量,只對shell所對應的進程生效,而對後臺的service無效。當用nohup方式執行程序的時候,因爲還是在shell對應的線程中,所以可以正常讀到設置的環境變量。

問題解決

解決辦法是,給service獨立設置環境變量。這裏有兩種方式。

1. Systemd Environment

Systemd Service的配置文件中,顯示指定Environment參數,來設置service運行時的環境變量,如:

[Unit]
Description=My SpringBoot Service
After=syslog.target

[Service]
User=nightfield
ExecStart=/path/to/mySpringBoot.jar
SuccessExitStatus=143
Restart=always
RestartSec=5
Environment="LD_LIBRARY_PATH=/path/to/cppFoler/"

[Install]
WantedBy=multi-user.target

這樣,service運行的時候,就可以正確讀到對應的變量。

2. ProcessBuild中指定environment()

ProcessBuild可以通過environment()方法,設置process運行的環境參數:

ProcessBuilder pb = new ProcessBuilder("java -version");
Map<String, String> env = pb.environment();
env.put("LD_LIBRARY_PATH", "/path/to/cppFoler/");
Process p = pb.start();

總結

通過這個問題,更深刻地學習了nohupservice的區別,profilebashrc的適用場景。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章