從nginx角度看伺服器多程序模型設計 二

2021-08-26 22:34:36 字數 4686 閱讀 7506

在ngx_master_process_cycle中要處理眾多的全域性變數,正是通過一些訊號處理函式設定這些變數,才會後面檢測到一些事件的發生。我們來看看都有哪些預定義的事件,以及他們是如何被處理的。

多數的事件來自於nginx的使用者,他們可能終止nginx,重啟,重讀配置等等,這些操作則主要依賴於訊號,在nginx官網,給出了比較全面的控制命令介紹,使用者可以通過他們來控制nginx的行為:

許多常用的操作,nginx通過一些定義的命令(「-s」後跟命令)來給使用者使用,避免了使用者去記住那些煩人的訊號值。如

日誌滾動, 「-s reopen」,即向master程序傳送-usr1訊號

快速停止nginx, 「-s stop」,即向master程序傳送-term或者-int訊號

從容關閉nginx,「-s quit」,即向master程序傳送-quit訊號

操作起來很簡單,兩種方式,要麼你針對master的程序id,傳送sighup訊號,或者在終端使用./nginx -s reload。

實際上使用reload命令,也是向master程序傳送sighup訊號,只不過是nginx幫我們去發這個訊號罷了。機理呢也很簡單,就是新起乙個nginx,找到master程序的pid,然後「kill -hup pid」。執行這個操作的nginx是一種特殊型別的程序,型別為ngx_process_signaller。

master程序收到sighup訊號時,master程序中的全域性變數ngx_reconfigure會置1:

if (ngx_reconfigure) 

ngx_log_error(ngx_log_notice, cycle->log, 0, "reconfiguring");

// 解析初始化失敗,後續的處理將繼續使用舊的

cycle = ngx_init_cycle(cycle);

if (cycle == null)

ngx_cycle = cycle;

ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,

ngx_core_module);

// 以ngx_process_just_respawn標記啟動,這個型別的作用是什麼呢?

ngx_start_worker_processes(cycle, ccf->worker_processes,

ngx_process_just_respawn);

ngx_start_cache_manager_processes(cycle, 1);

/* allow new processes to start */

ngx_msleep(100);

live = 1;

// 向其他worker程序傳送quit訊號,讓他們「和諧」退出。關於這個函式的具體內容,很直白,大家可以自己看。

ngx_signal_worker_processes(cycle,

ngx_signal_value(ngx_shutdown_signal));

}/*

* master做的工作就是這些,我們看看worker程序內部都發生了什麼變化,以及如何「和諧」退出。

* 當worker程序收到master發來的資訊,例如讓自己「和諧」退出,資訊中的command是ngx_cmd_quit,

* 那麼worker程序中的變數ngx_quit會置1。

*/if (ngx_quit)

}/*

* 關閉listen fd的作用在最開始官方給出的解釋已經很明確了,就是讓要舊的程序不再接收新連線,

* 等到舊連線都處理完之後,就可以退出了,也就是所謂「和諧」退出的含義了。在進入退出過程之後的後續處理,

* 還有一些細節,如ngx_exiting。*/

if (ngx_exiting) }/*

* 最近的一次事件處理沒有再新加timer或者原有的timer都已過期處理掉了,這樣也就意味真該程序把它該幹的都幹完了,

* 那現在就可以退出了。

*/if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)

}

通常的操作是,將原來的nginx執行檔案,備份一下,如"mv nginx nginx.bak", 之後將新編譯的檔案放到原sbin目錄下。向master程序傳送usr2訊號,即可以通過命令kill -usr2 `cat /usr/local/nginx/logs/nginx.pid`,經過這個處理之後,新舊程式會並存,並且都可以對外服務。如果此時需要讓舊程式退出,那麼可以向舊master程序傳送quit訊號,即使用kill -quit `cat /usr/local/nginx/logs/nginx.pid.oldbin`,其中nginx.pid.oldbin是在處理usr2訊號時生成的,其中含有舊master程序的pid。功能描述就是這樣,下面看看程式實現。

/*

* 在訊號處理函式,ngx_signal_handler中檢測到usr2訊號時,會將ngx_change_binary變數置1,

* 然後會在master cycle中實際去處理。

*/if (ngx_change_binary)

關於ngx_exec_new_binary,其實說來也簡單,它最終就是通過fork+execve的經典處理來實現的,不過在函式的開始部分有一些設定環境變數的處理,它有什麼作用呢?設想一下,如果新的二進位制檔案在啟動時必然要涉及bind埠的動作,而此時舊程序已經做了繫結,我們知道多個程序是不能同時繫結

同乙個位址和埠的,所以新的程序要避免這種情況發生。nginx的做法是,將原來的繫結得到的listen fd儲存在環境變數中,這樣在新程序初始化的過程中,通過函式ngx_add_inherited_sockets中就可以獲取listen fd來使用了,不必再次繫結。關於listen fd如何在環境變數中設定和獲取,大家可以參考相關**。

關於通過quit訊號讓舊程式退出的處理就不討論了,在分析reload「和諧」退出的時候已經講過。這裡有個細節要說一下:

新程式的執行,是通過舊程式通過ngx_spawn_process函式來處理的,這時的啟動型別是ngx_process_detached,意思是這個程序跟原來的舊程序的worker是不同的,分離的。舊程序中很多處理都需要跳過這種型別或者做專門的處理,因為新的程序也會在舊程序的ngx_processes中占用乙個位置。

接下來看日誌滾動。那什麼是日誌滾動?日誌滾動又叫日誌切割,它的作用是為了避免生成超大日誌檔案,這類檔案在讀取分析時會非常慢,而且會有佔滿磁碟的可能,如果將檔案每次以較小的尺寸產生,那麼在讀取和定期刪除時會方便很多。nginx的日誌滾動機制雖然不強,但是有值得借鑑的地方,我們來看:

首先要將有日誌檔案移走(或者重新命名),然後向master程序傳送usr1或者使用「-s reopen」命令。

跟其他的處理類似,master程序中有個名叫ngx_reopen的變數會在此時被置1,然後看處理:

if (ngx_reopen)

現在nginx的日誌滾動已經被很多人改過,例如通過cronolog,syslog-ng等,不僅實現了更好的切割,還能做到實時上傳到日誌伺服器上做處理。

好了,這裡再扯點細節,在我們上面的分析中,特別是在upgrade和reopen操作時,我們分別做了mv可執行檔案和日誌檔案的處理,可是這時候這兩者都是「正在使用」的,會不會影響服務或者引起一些異常呢?如程式正在使用,移動失敗或者寫日誌阻塞或者丟失。有這類疑問的同學,可以考慮下或者google下相關解釋,這裡就不說了。

最後呢,跟大家**下關於worker程序宕掉重啟的機制。

大家多數應該知道,在linux中,通過fork產生的子程序在coredump時,會向父程序傳送sigchld訊號,正是這種機制的存在,才使得重啟子程序的工作變的容易。看nginx的如何做的吧。

在訊號處理函式ngx_signal_handler中,會通過ngx_process_get_status,將master中ngx_processes裡面將該宕掉程序置位exited。另外master程序的ngx_reap會被置1,然後在master cycle的處理中:

if (ngx_reap)

關於ngx_reap_children細節,我們這裡總體上說一下,留給大家自己去研究就可以了。

在這個函式會找到那個宕掉的程序(即exited == 1的),然後判斷這個程序是原來的普通woker程序還是用來公升級的新程式,兩者的處理時不同的。

1. 普通worker程序

對於普通worker程序(即detached == 0),需要通告各個worker程序關於這個程序的資訊,讓他們保持同步,具體來說,就是通過ngx_cmd_close_channel來控制其他程序,讓他們修改各自ngx_processer中的相應條目。通告完這些資訊之後,master就需要重啟那個宕掉的worker了,在ngx_processes中的位置跟宕掉之前是一樣的,在建立完成之後,同樣需要將資訊廣播,以同步各個worker。

2. 公升級時的程序

首先pid檔案會被還原回來(前面講過的.oldbin檔案,還記得嗎?),然後修改一下資訊,例如該程序位於ngx_processes的最後乙個,那麼last_process減1,意味著該程序不會被重啟,應該這個程序你可以認為是公升級後的程式執行時的master程序,它自己的worker由它負責重啟,要是自己本身出了問題,那問題就大了,根本即沒有重啟的必要,去check**吧,呵呵。

還有一些小地方沒分析道,大家可以自己去看,相信有了上面這些做鋪墊,剩下的就不難了。good luck!

從nginx角度看伺服器多程序模型設計 一

多程序你可能很熟悉,也許有一套自己的使用習慣和方法。這東西沒有什麼權威建議,書上只是給出了基本知識點,至於具體怎麼去用,因人而異。nginx在多程序設計方面有很多值得學習和借鑑的東西,我認為是一套比較好的實現方案。你也許認為這東西很簡單,是老生常談的東西了,但是我這裡要提醒你一下,俗話道酒是陳的香,...

多程序伺服器

基於tcp實現多程序伺服器 伺服器端 1 建立套接字 include include int socket int domain,int type,int protocol domain 乙個位址描述。目前僅支援af inet格式,也就是說arpa internet位址格式。type 指定socke...

多程序伺服器

注意 包含了 wrap.c 和 wrap.h 檔案在上篇部落格中 server.c include include include in.h include include include include include include wrap.h define maxline 8192 defi...