NodeJS 不死行程
Linux 系統中的服務,通常都是以 Daemon 的形式存在,Daemon 泛指一種在背景執行的程序,通常可以長時間工作不會中斷。最近剛好要將一個 Node.JS 所設計的系統執行在 Linux 中,由於 Linux 在慣例上會把服務封裝為 Linux Service 並且將啟動器集中在 /etc/init.d 目錄中進行管理。能夠遵循 Linux 的慣例佈署系統是個好方法,免得換了一個人維護就得換顆腦袋。
談到 Node.JS 就頭大了,Node.JS 是單執行緒,當程式拋出例外錯誤而又沒有順利捕捉處理,就會造成程式中斷,服務也就同時中斷。今天順便介紹 forever 這個套件,顧名思義能讓您的 NodeJS 程序永恆地執行下去,安裝方式很簡單,如下:
# npm install forever -g
假設您的應用程式為 app.js,那麼安裝好 forever 之後就可以透過以下命令啟動:
$ forever start app.js
這樣當 app.js 錯誤中斷時,就會自動重新啟動,當然這個方法不是正解,能夠搭配其他方法監控服務會比較保險,像是 Log 與 Notification Email 等等機制。
Linux Service
接下來我們需要撰寫一支 Shell Script 來封裝 forever + Node.JS,這支 Script 需放置於 /etc/init.d 目錄中,這樣一來我們就可以統一透過以下 service 命命管理服務,假設檔名為 my-service,服務執行如下:
# service my-service start
# service my-service stop
# service my-service restart
# service my-service status
Script 內容如下 (GitHub),其中停用 Service 的方法,如果改用 forever stop 取代 kill 會更好:
#!/bin/sh ### BEGIN INIT INFO # Provides: # Required-Start: my-service # Required-Stop: my-service # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: My Linux Service ### END INIT INFO set -e SERVICE_NAME=`basename $0` PIDFILE=/var/run/myserv.pid LOGPATH="/var/log/${SERVICE_NAME}" FOREVER_BIN=`which forever` APP_PATH="/var/share/work-js/app.js" case $1 in start) if [ -e "${PIDFILE}" ]; then PID=`cat ${PIDFILE}` echo "Service is running already. (PID=${PID})" exit 1 fi if [ ! -d "${LOGPATH}" ]; then mkdir -p "${LOGPATH}" fi PID=`ps aux | grep ${APP_PATH} | head -n1 | awk '{print $2}'` ${FOREVER_BIN} -a -l "${LOGPATH}/service.log" -o "${LOGPATH}/out.log" -e "${LOGPATH}/error.log" start ${APP_PATH} > "${LOGPATH}/start.log" rm -rf ${PIDFILE} echo "${PID}" > ${PIDFILE} echo "Service ${SERVICE_NAME} start. PID=${PID}" ;; stop) if [ ! -e "${PIDFILE}" ]; then echo "Service is not running." else PID=`cat ${PIDFILE}` kill ${PID} || true rm -rf ${PIDFILE} echo "Service ${SERVICE_NAME} stop. PID=${PID}" fi ;; restart) $0 stop sleep 1 $0 start ;; status) if [ -e "${PIDFILE}" ]; then PID=`cat ${PIDFILE}` echo "Service is running. (PID=${PID})" else echo "Service is not running." fi ;; *) echo "Usage: ${0} {start|stop|restart|status}" exit 1 ;; esac