[nginx]句讀nginx服務(wù)啟動腳本

起因

之前需要設(shè)定一個服務(wù)的開機啟動腳本,但是遺憾的是我沒有很好的 Linux 系統(tǒng)使用和配置經(jīng)驗,操作系統(tǒng)不熟,Shell 腳本不熟,網(wǎng)上的教程和例子不少但是限于自身的基礎(chǔ),其實參考性不大;基于現(xiàn)有經(jīng)驗和使用狀況,其實nginx的服務(wù)啟動和周期管理不就是很好的例子?
通過句讀 nginx 的 init 腳本,擴展shell編程的知識點和編程結(jié)構(gòu);

回顧

首先我需要什么?
有一個本地的可執(zhí)行文件,啟動之后可以提供有限的http服務(wù).我需要他想nginx的服務(wù)一樣,開機啟動,不受會話啟動關(guān)閉的影響;守護(hù)進(jìn)程,前后臺的概念在過程中熟悉,先去看看怎么實踐;

nginx 服務(wù)操作

啟動命令
sudo /etc/init.d/nginx start
關(guān)閉命令
sudo /etc/init.d/nginx stop
查看狀態(tài)
sudo /etc/init.d/nginx status
重啟
sudo /etc/init.d/nginx restart
邏輯上看,是使用root權(quán)限來執(zhí)行一段存在絕對路徑下的,腳本,并且傳入了一個參數(shù),start,stop,status restart等等來執(zhí)行對應(yīng)的操作,交互層面模仿這樣就可以

sudo /etc/init.d/myservice {start|stop|status|restart}

開始句讀

開頭部分

#!/bin/sh

### BEGIN INIT INFO
# Provides:   nginx
# Required-Start:    $local_fs $remote_fs $network $syslog $named
# Required-Stop:     $local_fs $remote_fs $network $syslog $named
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the nginx web server
# Description:       starts nginx using start-stop-daemon
### END INIT INFO

第一行是 shell 腳本的魔法字符串,指定解釋器,好像所有的腳本都這樣子;而后是一大段注釋,init info 啟動信息,不是很明白,0 到 6 的數(shù)字貌似是運行級別,暫時先不管,被注釋的不會運行;

20170605 These comments are definitely not useless.
They are used to define LSB info, why I found it? because it has warning when I add it to init daemon use this

sudo update-rc.d myservice defaults

變量定義

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/nginx
NAME=nginx
DESC=nginx

接下的部分是變量設(shè)定部分,先看下本機系統(tǒng)中的PATH變量原來是什么,然后這里是直接指定了PATH
DAEMON的意思本身就是守護(hù)進(jìn)程吧,這里指定的nginx的二進(jìn)制程序?看看對應(yīng)目錄下的文件是什么意思?文件是真實存在的,而且應(yīng)該就是nginx的執(zhí)行文件,那我自己的可執(zhí)行文件也要配置到這個變量中吧
NAME 和 DESC 應(yīng)該只是下面腳本中的變量替換,作為服務(wù)的名字和描述,也要改成自己服務(wù)的名字

默認(rèn)配置運行

# Include nginx defaults if available
if [ -r /etc/default/nginx ]; then
    . /etc/default/nginx
fi

先要去看看 shell 當(dāng)中的 if 結(jié)構(gòu)是怎么寫的了,看不懂呀這個.....

shell 中的 if 結(jié)構(gòu)

分支結(jié)構(gòu)的基本形式是 if 后面跟上中括號,中括號里面放的是條件表達(dá)式,代表真假的布爾值,之后是一個分號和 then,真?zhèn)€分支代碼塊以 fi 結(jié)束

if [ ... ]; then
...
fi

方括號里的條件表達(dá)式的寫法有固定的格式,上文中的表達(dá)式具體的意思是檢測文件是不是可讀的-r /etc/default/nginx;
再來看條件成立的時候執(zhí)行的代碼塊中的命令,一個點,空格,加上文件,表示執(zhí)行這個文件, source file ; ./file ; . file 這幾個命令貌似都是執(zhí)行文件,但是還是略有不同,具體的區(qū)別暫時不去深究吧

回到句讀中,這一段的意思大概就是,查看這個配置文件是不是可讀的,如果是的話那就執(zhí)行這個配置文件,來看看這配置文件的內(nèi)容
/etc/defaults/nginx

 # Note: You may want to look at the following page before setting the ULIMIT.
#  http://wiki.nginx.org/CoreModule#worker_rlimit_nofile
# Set the ulimit variable if you need defaults to change.
#  Example: ULIMIT="-n 4096"
#ULIMIT="-n 4096"

內(nèi)容都被注釋掉了,就說沒有執(zhí)行任何東西

測試執(zhí)行文件

test -x $DAEMON || exit 0

這行代碼里面有幾點需要看,test命令是什么? -x 參數(shù)的意思是? 一個豎線是管道,兩個豎線又是什么? exit 0 是表示退出?

  • test 命令 : test 本身的作用是檢查文件類型并比較值,結(jié)合 -x 參數(shù)的作用就是檢查文件是否存在并且是不是有執(zhí)行的權(quán)限
  • 短路邏輯或 || : exit 0 左邊的兩個豎線 || 跟一般含義一樣是邏輯運算符,表示短路的邏輯或,當(dāng) || 左邊的 test 命令返回假的時候,右邊的 exit 0 退出命令才會執(zhí)行,表示腳本退出;
    結(jié)合上面的兩點來看,這個語句的意思就是,測試可執(zhí)行文件 DAEMON
    是否存在兵器可以執(zhí)行,如果不是的話就退出程序;

環(huán)境初始化

 . /lib/init/vars.sh
 . /lib/lsb/init-functions

這是執(zhí)行了兩個文件,看名字,一個是初始化變量定義,一個是初始化函數(shù),具體內(nèi)容中的確是有執(zhí)行一些變量和函數(shù)的定義,下面用到的時候在回頭看吧

嘗試獲取 PID

# Try to extract nginx pidfile
 PID=$(cat /etc/nginx/nginx.conf | grep -Ev '^\s*#' | awk 'BEGIN { RS="[;{}]" } { if ($1 == "pid") print $2 }' | head -n1)
if [ -z "$PID" ]
then
    PID=/run/nginx.pid
fi

首先肯定和這個文件 /etc/nginx/nginx.conf 有關(guān),cat 命令打印內(nèi)容,之后是用 grep 篩選了什么,然后交給 awk 命令 來執(zhí)行,最后是 head 命令;一個個來看

  • cat 命令很熟悉,就是吧文件的內(nèi)容打印到控制臺,也就是說,管道第一步是吧這個配置文件的內(nèi)容打印到控制臺
  • grep 只知道是篩選,會返回篩選的內(nèi)容所在的行這樣子,參數(shù) -Ev 的具體含義?后面跟的明顯是正則表達(dá)式,應(yīng)該是有關(guān)系的~~~
    • -E 參數(shù)的意思是后面跟隨的是正則表達(dá)式,而 -v 是表示反選,輸出的不是跟表達(dá)式匹配的,而是不匹配的;來看正則,意思會任意的空格開頭,而后是井號,意思是所有的注釋行?反選的話就是去掉所有的注釋
    • 到這里也就明顯,第一步打印配置文件,第二步,刪除所有的注釋行,接下來來到了第三步 awk
  • awk 命令沒有怎么接觸過,知道很強大;awk 是模式掃描和文本處理語言,這是一個語言?恩......好吧,看了一下, 本機的awk 實際上是一個指向 mawk 的鏈接,大概看了下語法,首先是指定要分割的分隔符集合,也就是 RS 等于的東西,然后針對分割之后的每行文本進(jìn)行處理,這里的意思就是,每一行都會被 RS 分割成兩部分應(yīng)該,第一個部分如果等于 PID 的話,那么就輸出第二部分; 這里就明了了,最后得到的就是存儲pid的文件;
  • head 獲取文件的前幾行, -n1 就是獲取第一行

在解析完配置文件之后,得到的是儲存pid的文件,所以之后在檢測一下,變量 pid 的長度是不是0, 如果沒有找到配置的文件,那么就是用默認(rèn)的 /run/nginx.pid

檢查ulimit

# Check if the ULIMIT is set in /etc/default/nginx
if [ -n "$ULIMIT" ]; then
    # Set the ulimits
    ulimit $ULIMIT
fi

因為這個文件中的ulimit配置選項被注釋掉了, -n 參數(shù)表示后面參數(shù)字符串的長度部位 0 的時候為真;所以這篇代碼不執(zhí)行, ulimit表示什么暫時不管

函數(shù)定義部分

#
# Function that starts the daemon/service
#
do_start()
{
    # Return
    #   0 if daemon has been started
    #   1 if daemon was already running
    #   2 if daemon could not be started
    start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \
        || return 1
    start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \
        $DAEMON_OPTS 2>/dev/null \
        || return 2
}

從函數(shù)的名字可以看出這是服務(wù)啟動的時候執(zhí)行的函數(shù),從注釋中也看出了返回值的具體含義,0-服務(wù)已經(jīng)開啟,1-服務(wù)已經(jīng)運行,2-服務(wù)起不起來
start-stop-daemon 是一個系統(tǒng)的命令,專門用來啟動關(guān)閉服務(wù)的,那么剩下的就是配置參數(shù)了,唯一的問題就是一直沒有找到那個 DAEMON_OPTS 變量定義在哪里
接下來的函數(shù)們就實現(xiàn)各自的功能了 start stop 等等

腳本參數(shù)解析

最后的一部分就是一個多路分支的結(jié)構(gòu)了,根據(jù)輸入的第一個參數(shù)來決定執(zhí)行什么函數(shù)

總結(jié)

看來一個 daemon 的維護(hù)需要的東西還是蠻多的,如果沒有配置需要載入的話,就需要兩個文件,一個是 /etc/init/myservice ,還有一個是必須的 pid 文件, /run/myservice.pid 照著寫應(yīng)該就是OK的,具體的執(zhí)行可以交給系統(tǒng)內(nèi)部的命令

文件原本- nginx服務(wù)腳本 /etc/init.d/nginx

#!/bin/sh

### BEGIN INIT INFO
# Provides:   nginx
# Required-Start:    $local_fs $remote_fs $network $syslog $named
# Required-Stop:     $local_fs $remote_fs $network $syslog $named
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the nginx web server
# Description:       starts nginx using start-stop-daemon
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/nginx
NAME=nginx
DESC=nginx

# Include nginx defaults if available
if [ -r /etc/default/nginx ]; then
    . /etc/default/nginx
fi

test -x $DAEMON || exit 0

. /lib/init/vars.sh
. /lib/lsb/init-functions

# Try to extract nginx pidfile
PID=$(cat /etc/nginx/nginx.conf | grep -Ev '^\s*#' | awk 'BEGIN { RS="[;{}]" } { if ($1 == "pid") print $2 }' | head -n1)
if [ -z "$PID" ]
then
    PID=/run/nginx.pid
fi

# Check if the ULIMIT is set in /etc/default/nginx
if [ -n "$ULIMIT" ]; then
    # Set the ulimits
    ulimit $ULIMIT
fi

#
# Function that starts the daemon/service
#
do_start()
{
    # Return
    #   0 if daemon has been started
    #   1 if daemon was already running
    #   2 if daemon could not be started
    start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \
        || return 1
    start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \
        $DAEMON_OPTS 2>/dev/null \
        || return 2
}

test_nginx_config() {
    $DAEMON -t $DAEMON_OPTS >/dev/null 2>&1
}

#
# Function that stops the daemon/service
#
do_stop()
{
    # Return
    #   0 if daemon has been stopped
    #   1 if daemon was already stopped
    #   2 if daemon could not be stopped
    #   other if a failure occurred
    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PID --name $NAME
    RETVAL="$?"

    sleep 1
    return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
    start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --name $NAME
    return 0
}

#
# Rotate log files
#
do_rotate() {
    start-stop-daemon --stop --signal USR1 --quiet --pidfile $PID --name $NAME
    return 0
}

#
# Online upgrade nginx executable
#
# "Upgrading Executable on the Fly"
# http://nginx.org/en/docs/control.html
#
do_upgrade() {
    # Return
    #   0 if nginx has been successfully upgraded
    #   1 if nginx is not running
    #   2 if the pid files were not created on time
    #   3 if the old master could not be killed
    if start-stop-daemon --stop --signal USR2 --quiet --pidfile $PID --name $NAME; then
        # Wait for both old and new master to write their pid file
        while [ ! -s "${PID}.oldbin" ] || [ ! -s "${PID}" ]; do
            cnt=`expr $cnt + 1`
            if [ $cnt -gt 10 ]; then
                return 2
            fi
            sleep 1
        done
        # Everything is ready, gracefully stop the old master
        if start-stop-daemon --stop --signal QUIT --quiet --pidfile "${PID}.oldbin" --name $NAME; then
            return 0
        else
            return 3
        fi
    else
        return 1
    fi
}

case "$1" in
    start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
            0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
            2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
    stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
            0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
            2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
    restart)
        log_daemon_msg "Restarting $DESC" "$NAME"

        # Check configuration before stopping nginx
        if ! test_nginx_config; then
            log_end_msg 1 # Configuration error
            exit 0
        fi

        do_stop
        case "$?" in
            0|1)
                do_start
                case "$?" in
                    0) log_end_msg 0 ;;
                    1) log_end_msg 1 ;; # Old process is still running
                    *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
            *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
    reload|force-reload)
        log_daemon_msg "Reloading $DESC configuration" "$NAME"

        # Check configuration before reload nginx
        #
        # This is not entirely correct since the on-disk nginx binary
        # may differ from the in-memory one, but that's not common.
        # We prefer to check the configuration and return an error
        # to the administrator.
        if ! test_nginx_config; then
            log_end_msg 1 # Configuration error
            exit 0
        fi

        do_reload
        log_end_msg $?
        ;;
    configtest|testconfig)
        log_daemon_msg "Testing $DESC configuration"
        test_nginx_config
        log_end_msg $?
        ;;
    status)
        status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $?
        ;;
    upgrade)
        log_daemon_msg "Upgrading binary" "$NAME"
        do_upgrade
        log_end_msg 0
        ;;
    rotate)
        log_daemon_msg "Re-opening $DESC log files" "$NAME"
        do_rotate
        log_end_msg $?
        ;;
    *)
        echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}" >&2
        exit 3
        ;;
esac
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子苍柏,更是在濱河造成了極大的恐慌妨马,老刑警劉巖奈虾,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗦明,死亡現(xiàn)場離奇詭異,居然都是意外死亡爬舰,警方通過查閱死者的電腦和手機禀横,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門屁药,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柏锄,你說我怎么就攤上這事酿箭「纯鳎” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵缭嫡,是天一觀的道長缔御。 經(jīng)常有香客問我,道長械巡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任饶氏,我火速辦了婚禮讥耗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疹启。我一直安慰自己古程,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布喊崖。 她就那樣靜靜地躺著挣磨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荤懂。 梳的紋絲不亂的頭發(fā)上茁裙,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音节仿,去河邊找鬼晤锥。 笑死,一個胖子當(dāng)著我的面吹牛廊宪,可吹牛的內(nèi)容都是我干的矾瘾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼箭启,長吁一口氣:“原來是場噩夢啊……” “哼壕翩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起傅寡,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤放妈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后荐操,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體大猛,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年淀零,在試婚紗的時候發(fā)現(xiàn)自己被綠了挽绩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡驾中,死狀恐怖唉堪,靈堂內(nèi)的尸體忽然破棺而出模聋,到底是詐尸還是另有隱情,我是刑警寧澤唠亚,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布链方,位于F島的核電站,受9級特大地震影響灶搜,放射性物質(zhì)發(fā)生泄漏祟蚀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一割卖、第九天 我趴在偏房一處隱蔽的房頂上張望前酿。 院中可真熱鬧,春花似錦鹏溯、人聲如沸罢维。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肺孵。三九已至,卻和暖如春颜阐,著一層夾襖步出監(jiān)牢的瞬間平窘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工凳怨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留初婆,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓猿棉,卻偏偏與公主長得像磅叛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子萨赁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容