1. 程式人生 > >深入理解Nginx 模組開發與架構解析-陶輝 讀書筆記

深入理解Nginx 模組開發與架構解析-陶輝 讀書筆記

前言

1. nginx是一個優秀的事件驅動框架,nginx非常適合開發在傳輸層以TCP對外提供服務的伺服器程式。基於nginx框架開發程式有5個優勢:

* nginx將網路、磁碟及定時器等非同步事件的驅動都做了非常好的封裝,基於它開發將可以忽略這些事情處理的細節

* nginx封裝了許多平臺無關的介面、容器,適用於跨平臺開發

* 優秀的模組化設計,使得開發者可以輕易的複用各種已有的模組,其中既包括基本的讀取配置、記錄日誌等模組,也包括處理請求的諸如http、mail等高階功能模組

* nginx是作為伺服器來設計其框架的,因此,它在伺服器程序的管理上相當出色,基於它開發伺服器程式可以輕鬆地實現程式的動態升級,子程序的監控、管理,配置項的動態修改生效等。

* nginx充分考慮到各作業系統所擅長的“絕活”,能夠使用特殊的系統呼叫更高效地完成任務時,絕不會去使用低效的通用介面。尤其對於Linux作業系統,nginx不遺餘力地做了大量優化。

第一部分

nginx能幫我們做什麼

第一章 

研究nginx前的準備工作

1. 2012年,nginx榮獲年度雲端計算開發獎,併成為世界第二大web伺服器

1.1 nginx是什麼?

1. web伺服器的基本功能:基於REST架構風格,以統一資源描述符(uniform resource identifier, URI)或統一資源定位符(uniform resource locator, URL)作為溝通依據,通過http為瀏覽器等客戶端程式提供各種網路服務。

2. nginx的競爭對手---Apache,Lighttpd, tomcat,jetty, IIS。他們都是web伺服器,或者叫做WWW伺服器。、

3. nginx是一個跨平臺的web伺服器,可執行在Linux,FreeBSD,Solaris,AIX,MacOS,Windows等作業系統上,並且它還可以使用當前作業系統特有的一些高效API來提高自己的效能

1.2 為什麼選擇nginx

1. nginx具備以下特點

* 更快

* 高擴充套件性

* 高可靠性

* 低記憶體消耗

* 單機支援10萬以上的併發連線

* 熱部署

* 最自由的BSD許可協議

nginx的核心優點還是他能夠在支援高併發請求的同時保持高效的服務

1.3.1 Linux作業系統

1. 系統必須是核心為Linux 2.6 及以上版本的作業系統,因為Linux 2.6及以上核心才支援epoll,而在Linux上使用select或poll來解決事件的多路複用,是無法解決高併發壓力問題的。

2. uname -a 查詢Linux核心版本

1.3.3 磁碟目錄

1. nginx編譯階段產生的中間檔案存放目錄

該目錄用於放置configure命令執行後所產生的原始檔及目錄,以及make命令執行後生成的目標檔案和最終連線成功的二進位制檔案。預設情況下,configure命令會將該目錄命名為objs

第十四章

程序間的通訊機制

1.  nginx由一個master程序和多個worker程序組成,但master程序或者worker程序中並不會再建立執行緒(nginx的多執行緒機制一直停留在測試狀態,雖然不排除未來nginx可能釋出支援多執行緒版本的可能性)

14.1

1. nginx框架使用了3種傳遞訊息傳遞方式:共享記憶體、套接字、訊號

2. nginx主要使用了3種同步方式:原子操作、訊號量、檔案鎖

14.2 共享記憶體

1. 共享記憶體是Linux下提供的最基本的程序間通訊方法,它通過mmap或者shmget系統呼叫在記憶體中建立了一塊連續的線性地址空間,而通過munmap或者shmdt系統呼叫可以釋放這塊記憶體

使用共享記憶體的好處是當多個程序使用同一塊共享記憶體時,在任何一個程序修改了共享記憶體中的內容後,其他程序通過訪問這段共享記憶體都能夠得到修改後的內容

2. nginx各程序間共享資料的主要方式就是使用共享記憶體,在使用共享記憶體時,nginx一般是由master程序建立,在master程序fork出worker子程序後,所有的程序開始使用這塊記憶體中的資料

14.3 原子操作

1. 能夠執行原子操作的原子變數只有整型,包括無符號整型ngx_atomic_uint_t和有符號整型ngx_atomic_t。這兩種型別都使用了volatile關鍵字告訴C編譯器不要做優化。

14.3.1 不支援原子庫下的原子操作

14.3.3 自旋鎖

1. 基於原子操作,nginx實現了一個自旋鎖。

2. 自旋鎖是一種非睡眠鎖,也就是說,某程序如果試圖獲得自旋鎖,當發現鎖已經被其他程序獲得時,那麼不會使得當前程序進入睡眠狀態,而是始終保持程序在可執行狀態,每當核心排程到這個程序執行時就持續檢查是否可以獲取到鎖。在拿不到鎖時,這個程序的程式碼將會一直在自旋鎖程式碼處執行,直到其他程序釋放了鎖且當前程序獲取了鎖後,程式碼才會繼續向下執行。

3. 自旋鎖主要是為多處理器作業系統而設定的,它要解決的共享資源保護場景就是程序使用鎖的時間非常短,如果鎖的使用時間很久,自旋鎖會不太合適,那麼它會佔用大量的CPU資源

4. ngx_spinlock方法是非常高效的自旋鎖,它充分考慮了單處理器和多處理器的系統,對於持有鎖時間非常短的場景很有效率

14.4 nginx頻道

1. ngx_channel_t頻道是nginx master程序與worker程序之間通訊的常用工具,它是使用本機套接字實現的。

int socketpair( int d, int type, int portocol, int sv[2]); 建立父子程序間使用的套接字

2. 當socketpair執行成功時,sv[2]這兩個套接字具備下列關係:向sv[0]套接字寫入資料,將可以從sv[1]套接字中讀取到剛寫入的資料;同樣,向sv[1]套接字寫入資料,也可以從sv[0]中讀取到寫入的資料。

通常,在父子程序通訊前,會先呼叫socketpair方法建立這樣一組套接字,在呼叫fork方法創建出子程序後,將會在父程序中關閉sv[1]套接字,僅使用sv[0]套接字用於向子程序傳送資料以及接受子程序傳送來的資料;而在子程序中則關閉sv[0]套接字,僅使用sv[1]套接字既可以接受父程序發來的資料,也可以向父程序傳送資料

3. master程序如何監控、管理worker子程序?

每次派生一個子程序之前,也就是fork之前,都會先呼叫socketpair方法,在nginx派生子程序ngx_spawn_process方法中,會首先派生基於TCP的套接字,master正是通過socketpair產生的套接字傳送命令的。

14.5 訊號

1. Linux提供了以訊號傳遞程序間訊息的機制,nginx在管理master程序和worker程序時大量使用了訊號。

2. 訊號和訊號量是完全不同的概念,訊號量僅用於同步程式碼段,而訊號則用於傳遞訊息。一個程序可以向另外一個程序或者另外一組程序傳送訊號訊息,通知目標程序執行特定的程式碼

./nginx -s reload

14.6 訊號量

1. 訊號量是用來保證兩個或多個程式碼段不被併發訪問,是一種保證共享資源有序訪問的工具,使用訊號量作為互斥鎖有可能導致程序睡眠,因此,要謹慎使用。

2. int sem_init( sem_t* sem, int pshared, unsigned int value);

初始化訊號量, pshared為0時表示執行緒間同步,為1時表示程序間同步。

3. int sem_destroy(sem_t *sem)

銷燬訊號量

4. int sem_post(sem_t *sem);

訊號量的值加一

5. int sem_wait(sem_t * sem);

訊號量的值減一

14.7 檔案鎖

1. Linux核心提供了基於檔案的互斥鎖

int fcntl(int fd, int cmd, struct flock* lock);

fd 是開啟的檔案控制代碼,cmd 表示執行的鎖操作,lock描述了這個鎖的資訊。

2. nginx.conf檔案中的lock_file配置項指定的檔案路徑,就是用於檔案互斥鎖的。

3. 對於檔案鎖,nginx封裝了3個方法:ngx_trylock_fd實現了非阻塞程序,不會使程序進入睡眠狀態的互斥鎖

ngx_lock_fd 阻塞互斥鎖

ngx_unlock_fd 釋放互斥鎖

14.8 互斥鎖

總結:nginx是一個能夠併發處理幾十萬甚至幾百萬個TCP連線的高效能伺服器,因此,在進行程序間通訊時,必須充分考慮到不能過分影響正常請求的處理

p20