Nginx学习笔记(一)
Nginx初識
Nginx架構(gòu)
Nginx在后臺運行包含一個master進(jìn)程和多個worker進(jìn)程。所以Nginx一般以<u>多進(jìn)程</u>方式運行,且支持**<u>多線程</u>**,在調(diào)試時也可以選擇以<u>單進(jìn)程</u>方式運行。
master進(jìn)程用來管理worker進(jìn)程,可以接受外界信號,向worker發(fā)送信號,監(jiān)控worker狀態(tài)。worker平等且獨立處理各個請求,worker進(jìn)程的個數(shù)可以設(shè)置,一般與CPU核數(shù)一致。
Nginx進(jìn)程模型如圖1.1:
<center>圖1.1</center>
Nginx采用**<u>異步非阻塞</u>**的方式處理請求。完整的請求過程如下:
事件進(jìn)入后,若沒有準(zhǔn)備好,返回EAGAIN,設(shè)置超時時間,同一時間內(nèi)監(jiān)控多個事件,在此期間可以處理其他事情,在此時間內(nèi)有事件準(zhǔn)備好就返回。
基礎(chǔ)概念
connection
connection是對TCP連接的封裝,包括連接的socket,讀事件,寫事件。http請求也是建立在connection上的,所以Nginx可以作為web服務(wù)器,也可以作為郵件服務(wù)器。
Nginx處理連接的過程如下:
Nginx在啟動時會解析配置文件,得到監(jiān)聽端口和IP地址,然后在master進(jìn)程中初始化socket,fork多個子進(jìn)程。此時客戶端可以發(fā)起連接,當(dāng)客戶端與服務(wù)器三次握手建立連接后,某一子進(jìn)程會accept成功,得到建立好的連接的socket,然后創(chuàng)建對連接的封裝,進(jìn)行事件的處理與數(shù)據(jù)交換。最后,Nginx或客戶端主動關(guān)閉連接。
在操作系統(tǒng)中,通過ulimit -n,我們可以得到一個進(jìn)程所能打開的fd(file descriptor)的最大數(shù),即nofile,這會限制我們進(jìn)程的最大連接數(shù),影響程序所能支持的最大并發(fā)數(shù)。Nginx通過設(shè)置worker_connections來設(shè)置每個進(jìn)程的支持的最大連接數(shù),如果該值大于noflie,那實際最大連接數(shù)就是noflie。Nginx在實現(xiàn)時,通過連接池管理,每個worker進(jìn)程有一個獨立的連接池,連接池大小是worker_connections。Nginx所能建立的最大連接數(shù)應(yīng)是:worker_connections * worker_processes,對于HTTP請求本地資源也是同樣,如果HTTP作為反向代理,最大并發(fā)量應(yīng)為:worker_connections * worker_processes / 2。因為作為反向代理服務(wù)器,每個并發(fā)會建立與客戶端的連接和與后端服務(wù)的連接,會占用兩個連接。
request
request指的是HTTP請求,在Nginx中的數(shù)據(jù)結(jié)構(gòu)是ngx_http_request_t。ngx_http_request_t是對一個HTTP請求的封裝,包括請求行,請求頭,請求體,響應(yīng)行,響應(yīng)頭,響應(yīng)體。
Nginx中一個HTTP請求的生命周期如圖1.2.2
<center>圖1.2.2</center>
keepalive
HTTP請求是請求應(yīng)答式的,如果能知道每個請求頭與響應(yīng)體的長度,就可以在一個連接上執(zhí)行多個請求,這就是長連接。
如果客戶端的請求頭中的 connection為close,則表示客戶端需要關(guān)掉長連接,如果為 keep-alive,則客戶端需要打開長連接,如果客戶端的請求中沒有 connection 這個頭,那么根據(jù)協(xié)議,如果是 http1.0,則默認(rèn)為 close,如果是 http1.1,則默認(rèn)為 keep-alive。當(dāng) Nginx 設(shè)置了 keepalive 等待下一次的請求時,同時也會設(shè)置一個最大等待時間,這個時間是通過選項 keepalive_timeout 來配置的,如果配置為 0,則表示關(guān)掉 keepalive,此時,http 版本無論是 1.1 還是 1.0,客戶端的 connection 不管是 close 還是 keepalive,都會強制為 close。一般來說,當(dāng)客戶端的一次訪問,需要多次訪問同一個 server 時,打開 keepalive 的優(yōu)勢非常大,比如圖片服務(wù)器。打開 keepalive 會大量減少 time-wait 的數(shù)量。
pipe
在 http1.1 中,引入了一種新的特性,即 pipeline,流水線作業(yè),它可以看作為 keepalive 的一種升華,因為 pipeline 也是基于長連接的,目的就是利用一個連接做多次請求。如果客戶端要提交多個請求,對于keepalive來說,那么第二個請求,必須要等到第一個請求的響應(yīng)接收完全后,才能發(fā)起,這和 TCP 的停止等待協(xié)議是一樣的,得到兩個響應(yīng)的時間至少為2*RTT。而對 pipeline 來說,客戶端不必等到第一個請求處理完后,就可以馬上發(fā)起第二個請求,得到兩個響應(yīng)的時間可能能夠達(dá)到1*RTT。
Nginx 對 pipeline 中的多個請求的處理不是并行的,依然是一個請求接一個請求的處理,只是在處理第一個請求的時候,客戶端就可以發(fā)起第二個請求。Nginx 利用 pipeline 減少了處理完一個請求后,等待第二個請求的請求頭數(shù)據(jù)的時間。
lingering_close
lingering_close是延遲關(guān)閉,也就是說,當(dāng) Nginx 要關(guān)閉連接時,并非立即關(guān)閉連接,而是先關(guān)閉 tcp 連接的寫,再等待一段時間后再關(guān)掉連接的讀。
基本數(shù)據(jù)結(jié)構(gòu)
ngx_str_t
帶長度的字符串結(jié)構(gòu),原型如下:
typedef struct { size_t len; u_char *data; } ngx_str_t;
在結(jié)構(gòu)體當(dāng)中,data 指向字符串?dāng)?shù)據(jù)的第一個字符,字符串的結(jié)束用長度來表示,而不是由'\\0'來表示結(jié)束。基于此特性,在 Nginx 中,必須謹(jǐn)慎的去修改一個字符串。
這樣做有兩點好處,首先,通過長度來表示字符串長度,減少計算字符串長度的次數(shù)。其次,Nginx 可以重復(fù)引用一段字符串內(nèi)存,data 可以指向任意內(nèi)存,長度表示結(jié)束,而不用去 copy 一份自己的字符串(因為如果要以'\\0'結(jié)束,而不能更改原字符串,所以勢必要 copy 一段字符串),減少了很多不必要的內(nèi)存分配與拷貝。
Nginx 提供的操作字符串相關(guān)的 API 如下:
#define ngx_string(str) { sizeof(str) - 1, (u_char *) str }
ngx_string(str) 是一個宏,它通過一個以'\\0'結(jié)尾的普通字符串 str 構(gòu)造一個 Nginx 的字符串,鑒于其中采用 sizeof 操作符計算字符串長度,因此參數(shù)必須是一個常量字符串。
#define ngx_null_string { 0, NULL }
定義變量時,使用 ngx_null_string 初始化字符串為空字符串,長度為 0,data 為 NULL。
#define ngx_str_set(str, text) (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text
ngx_str_set 用于設(shè)置字符串 str 為 text,由于使用 sizeof 計算長度,故 text 必須為常量字符串。
#define ngx_str_null(str) (str)->len = 0; (str)->data = NULL
ngx_str_null 用于設(shè)置字符串 str 為空串,長度為 0,data 為 NULL。
ngx_pool_t
ngx_pool_t 提供了一種機制,幫助管理一系列的資源(如內(nèi)存,文件等),使得對這些資源的使用和釋放統(tǒng)一進(jìn)行,免除了使用過程中考慮到對各種各樣資源的什么時候釋放,是否遺漏了釋放的擔(dān)心。
typedef struct ngx_pool_s ngx_pool_t; struct ngx_pool_s { ngx_pool_data_t d; size_t max; ngx_pool_t *current; ngx_chain_t *chain; ngx_pool_large_t *large; ngx_pool_cleanup_t *cleanup; ngx_log_t *log; };
ngx_pool_t 的相關(guān)操作如下:
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
創(chuàng)建一個初始節(jié)點大小為 size 的 pool,log 為后續(xù)在該 pool 上進(jìn)行操作時輸出日志的對象。 需要說明的是 size 的選擇,size 的大小必須小于等于 NGX_MAX_ALLOC_FROM_POOL,且必須大于 sizeof(ngx_pool_t)。
void *ngx_palloc(ngx_pool_t *pool, size_t size);
從這個 pool 中分配一塊為 size 大小的內(nèi)存。函數(shù)分配的內(nèi)存的起始地址按照 NGX_ALIGNMENT 進(jìn)行了對齊。對齊操作會提高系統(tǒng)處理的速度,但會造成少量內(nèi)存的浪費。
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
從這個 pool 中分配一塊為 size 大小的內(nèi)存。但是此函數(shù)分配的內(nèi)存并沒有像上面的函數(shù)那樣進(jìn)行過對齊。
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
該函數(shù)也是分配size大小的內(nèi)存,并且對分配的內(nèi)存塊進(jìn)行了清零。
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
按照指定對齊大小 alignment 來申請一塊大小為 size 的內(nèi)存。此處獲取的內(nèi)存不管大小都將被置于大內(nèi)存塊鏈中管理。
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);
對于被置于大塊內(nèi)存鏈,也就是被 large 字段管理的一列內(nèi)存中的某塊進(jìn)行釋放。該函數(shù)的實現(xiàn)是順序遍歷 large 管理的大塊內(nèi)存鏈表,所以效率比較低下。
ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
ngx_pool_t 中的 cleanup 字段管理著一個特殊的鏈表,該鏈表的每一項都記錄著一個特殊的需要釋放的資源。對于這個鏈表中每個節(jié)點所包含的資源如何去釋放,是自說明的。這也就提供了非常大的靈活性。這個鏈表每個節(jié)點的類型如下:
typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t; typedef void (*ngx_pool_cleanup_pt)(void *data); struct ngx_pool_cleanup_s { ngx_pool_cleanup_pt handler; void *data; ngx_pool_cleanup_t *next; };
- data: 指明了該節(jié)點所對應(yīng)的資源。
- handler: 是一個函數(shù)指針,指向一個可以釋放 data 所對應(yīng)資源的函數(shù)。該函數(shù)只有一個參數(shù),就是 data。
- next: 指向該鏈表中下一個元素。
void ngx_destroy_pool(ngx_pool_t *pool);
該函數(shù)就是釋放 pool 中持有的所有內(nèi)存,以及依次調(diào)用 cleanup 字段所管理的鏈表中每個元素的 handler 字段所指向的函數(shù),來釋放掉所有該 pool 管理的資源。并且把 pool 指向的 ngx_pool_t 也釋放掉了。
void ngx_reset_pool(ngx_pool_t *pool);
該函數(shù)釋放 pool 中所有大塊內(nèi)存鏈表上的內(nèi)存,小塊內(nèi)存鏈上的內(nèi)存塊都修改為可用。
ngx_array_t
ngx_array_t 是 Nginx 的數(shù)組結(jié)構(gòu)。
typedef struct ngx_array_s ngx_array_t; struct ngx_array_s { void *elts; size_t size; ngx_pool_t *pool; };
- elts: 指向?qū)嶋H的數(shù)據(jù)存儲區(qū)域。
- nelts: 數(shù)組實際元素個數(shù)。
- size: 數(shù)組單個元素的大小,單位是字節(jié)。
- nalloc: 數(shù)組的容量。表示該數(shù)組在不引發(fā)擴容的前提下,可以最多存儲的元素的個數(shù)。當(dāng) nelts 增長到達(dá) nalloc 時,如果再往此數(shù)組中存儲元素,則會引發(fā)數(shù)組的擴容。數(shù)組的容量將會擴展到原有容量的 2 倍大小。實際上是分配新的一塊內(nèi)存,新的一塊內(nèi)存的大小是原有內(nèi)存大小的 2 倍。原有的數(shù)據(jù)會被拷貝到新的一塊內(nèi)存中。
- pool: 該數(shù)組用來分配內(nèi)存的內(nèi)存池。
ngx_array_t 相關(guān)操作函數(shù)如下:
ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
創(chuàng)建一個新的數(shù)組對象,并返回這個對象。
-
p: 數(shù)組分配內(nèi)存使用的內(nèi)存池;
-
n: 數(shù)組的初始容量大小,即在不擴容的情況下最多可以容納的元素個數(shù)。
-
size: 單個元素的大小,單位是字節(jié)。
void ngx_array_destroy(ngx_array_t *a);
銷毀該數(shù)組對象,并釋放其分配的內(nèi)存回內(nèi)存池。
void *ngx_array_push(ngx_array_t *a);
在數(shù)組 a 上新追加一個元素,并返回指向新元素的指針。
void *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);
在數(shù)組 a 上追加 n 個元素,并返回指向這些追加元素的首個元素的位置的指針。
ngx_hash_t
ngx_hash_t 是 Nginx 的 hash 表。
ngx_hash_t 的實現(xiàn)有幾個顯著的特點:
ngx_hash_t 的初始化如下:
ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts);
names 是初始化一個 ngx_hash_t 所需要的所有 key 的一個數(shù)組。而 nelts 就是 key 的個數(shù)。
以下是ngx_hash_init_t 類型,該類型提供了初始化一個 hash 表所需要的一些基本信息。
typedef struct { ngx_hash_t *hash; ngx_hash_key_pt key; ngx_uint_t max_size; ngx_uint_t bucket_size; char *name; ngx_pool_t *pool; ngx_pool_t *temp_pool; } ngx_hash_init_t;
- hash: 該字段如果為 NULL,那么調(diào)用完初始化函數(shù)后,該字段指向新創(chuàng)建出來的 hash 表。如果該字段不為 NULL,那么在初始的時候,所有的數(shù)據(jù)插入了這個字段所指的 hash 表中。
- key: 指向從字符串生成 hash 值的 hash 函數(shù)。Nginx 的源代碼中提供了默認(rèn)的實現(xiàn)函數(shù) ngx_hash_key_lc。
- max_size: hash 表中的桶的個數(shù)。該字段越大,元素存儲時沖突的可能性越小,每個桶中存儲的元素會更少,則查詢的速度更快。這個值越大,越造成內(nèi)存的浪費也越大。
- bucket_size: 每個桶的最大限制大小,單位是字節(jié)。如果在初始化一個 hash 表的時候,發(fā)現(xiàn)某個桶里面無法存的下所有屬于該桶的元素,則 hash 表初始化失敗。
- name: 該 hash 表的名字。
- pool: 該 hash 表分配內(nèi)存使用的 pool。
- temp_pool: 該 hash 表使用的臨時 pool,初始化完成后,該 pool 可以被釋放和銷毀。
存儲 hash 表 key 的數(shù)組的結(jié)構(gòu)如下:
typedef struct { ngx_str_t key; ngx_uint_t key_hash; void *value; } ngx_hash_key_t;
ngx_hash_wildcard_t
Nginx 為了處理帶有通配符的域名的匹配問題,實現(xiàn)了 ngx_hash_wildcard_t 這樣的 hash 表。它可以支持兩種類型的帶有通配符的域名。一種是通配符在前的,例如:*.abc.com,這樣的 key,可以匹配 www.abc.com,qqq.www.abc.com 之類的。另外一種是通配符在末尾的,例如:mail.xxx.*,請?zhí)貏e注意通配符在末尾的不像位于開始的通配符可以被省略掉。這樣的通配符,可以匹配 mail.xxx.com、mail.xxx.com.cn、mail.xxx.net 之類的域名。
ngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts);
該函數(shù)用來構(gòu)建一個可以包含通配符 key 的 hash 表。
void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);
該函數(shù)查詢包含通配符在前的 key 的 hash 表的。
- hwc: hash 表對象的指針。
- name: 需要查詢的域名,例如: www.abc.com。
- len: name 的長度。
該函數(shù)返回匹配的通配符對應(yīng) value。如果沒有查到,返回 NULL。
void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);
該函數(shù)查詢包含通配符在末尾的 key 的 hash 表的。
ngx_hash_combined_t
組合類型 hash 表,該 hash 表的定義如下:
typedef struct { ngx_hash_t hash; ngx_hash_wildcard_t *wc_head; ngx_hash_wildcard_t *wc_tail; } ngx_hash_combined_t;
該類型實際上包含了三個 hash 表,一個普通 hash 表,一個包含前向通配符的 hash 表和一個包含后向通配符的 hash 表。
該 hash 表的查詢,Nginx 提供了一個方便的函數(shù) ngx_hash_find_combined。
void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name, size_t len);
該函數(shù)在此組合 hash 表中,依次查詢其三個子 hash 表,看是否匹配,一旦找到,立即返回查找結(jié)果,如果有多個可能匹配,則只返回第一個匹配的結(jié)果。
- hash: 此組合 hash 表對象。
- key: 根據(jù) name 計算出的 hash 值。
- name: key 的具體內(nèi)容。
- len: name 的長度。
返回查詢的結(jié)果,未查到則返回 NULL。
ngx_list_t
他是一個類似 list 的數(shù)據(jù)結(jié)構(gòu)。它的節(jié)點不像我們常見的 list 的節(jié)點,只能存放一個元素,ngx_list_t 的節(jié)點實際上是一個固定大小的數(shù)組。
list定義如下:
typedef struct { ngx_list_part_t *last; ngx_list_part_t part; size_t size; ngx_uint_t nalloc; ngx_pool_t *pool; } ngx_list_t;
- last: 指向該鏈表的最后一個節(jié)點。
- part: 該鏈表的首個存放具體元素的節(jié)點。
- size: 鏈表中存放的具體元素所需內(nèi)存大小。
- nalloc: 每個節(jié)點所含的固定大小的數(shù)組的容量。
- pool: 該 list 使用的分配內(nèi)存的 pool。
節(jié)點的定義如下:
typedef struct ngx_list_part_s ngx_list_part_t; struct ngx_list_part_s { void *elts; ngx_uint_t nelts; ngx_list_part_t *next; };
- elts: 節(jié)點中存放具體元素的內(nèi)存的開始地址。
- nelts: 節(jié)點中已有元素個數(shù)。這個值是不能大于鏈表頭節(jié)點 ngx_list_t 類型中的 nalloc 字段的。
- next: 指向下一個節(jié)點。
ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);
該函數(shù)創(chuàng)建一個 ngx_list_t 類型的對象,并對該 list 的第一個節(jié)點分配存放元素的內(nèi)存空間。
- pool: 分配內(nèi)存使用的 pool。
- n: 每個節(jié)點(ngx_list_part_t)固定長度的數(shù)組的長度,即最多可以存放的元素個數(shù)。
- size: 每個元素所占用的內(nèi)存大小。
- 返回值: 成功返回指向創(chuàng)建的 ngx_list_t 對象的指針,失敗返回 NULL。
void *ngx_list_push(ngx_list_t *list);
該函數(shù)在給定的 list 的尾部追加一個元素,并返回指向新元素存放空間的指針。如果追加失敗,則返回 NULL。
ngx_queue_t
ngx_queue_t 是 Nginx 中的雙向鏈表。
typedef struct ngx_queue_s ngx_queue_t; struct ngx_queue_s { ngx_queue_t *prev; ngx_queue_t *next; };
不同于教科書中將鏈表節(jié)點的數(shù)據(jù)成員聲明在鏈表節(jié)點的結(jié)構(gòu)體中,ngx_queue_t 只是聲明了前向和后向指針。
Nginx配置系統(tǒng)
配置系統(tǒng)
Nginx的配置系統(tǒng)由一個祝配置文件和其他輔助配置文件構(gòu)成。這些文件均是純文本文件,全位于安裝目錄下的conf目錄下。
配置文件中以#開始的行,或前面有若干空格或者 TAB,然后再跟#的行,都被認(rèn)為是注釋。
在 nginx.conf 中,包含若干配置項。每個配置項由配置指令和指令參數(shù) 2 個部分構(gòu)成。指令參數(shù)也就是配置指令對應(yīng)的配置值。
指令概述
配置指令是一個字符串,可以用單引號或者雙引號括起來,也可以不括。但是如果配置指令包含空格,一定要引起來。
指令參數(shù)
指令的參數(shù)使用一個或者多個空格或者 TAB 字符與指令分開。指令的參數(shù)有一個或者多個 TOKEN 串組成。TOKEN 串之間由空格或者 TAB 鍵分隔。
TOKEN 串分為簡單字符串或者是復(fù)合配置塊。復(fù)合配置塊即是由大括號括起來的一堆內(nèi)容。一個復(fù)合配置塊中可能包含若干其他的配置指令。
如果一個配置指令的參數(shù)全部由簡單字符串構(gòu)成,也就是不包含復(fù)合配置塊,那么我們就說這個配置指令是一個簡單配置項,否則稱之為復(fù)雜配置項。例如下面這個是一個簡單配置項:
error_page 500 502 503 504 /50x.html;對于簡單配置,配置項的結(jié)尾使用分號結(jié)束。對于復(fù)雜配置項,包含多個 TOKEN 串的,一般都是簡單 TOKEN 串放在前面,復(fù)合配置塊一般位于最后,而且其結(jié)尾,并不需要再添加分號。例如下面這個復(fù)雜配置項:
location {root /home/jizhao/nginx-book/build/html;index index.html index.htm; }指令上下文
nginx.conf 中的配置信息,根據(jù)其邏輯上的意義,對它們進(jìn)行分類,也就是分成了多個作用域,或稱之為配置指令上下文。不同的作用域含有一個或者多個配置項。
當(dāng)前 Nginx 支持的幾個指令上下文:
- main: Nginx 在運行時與具體業(yè)務(wù)功能(比如http服務(wù)或者email服務(wù)代理)無關(guān)的一些參數(shù),比如工作進(jìn)程數(shù),運行的身份等。
- http: 與提供 http 服務(wù)相關(guān)的一些配置參數(shù)。例如:是否使用 keepalive ,是否使用gzip進(jìn)行壓縮等。
- server: http 服務(wù)上支持若干虛擬主機。每個虛擬主機一個對應(yīng)的 server 配置項,配置項里面包含該虛擬主機相關(guān)的配置。在提供 mail 服務(wù)的代理時,也可以建立若干 server,每個 server 通過監(jiān)聽的地址來區(qū)分。
- location: http 服務(wù)中,某些特定的URL對應(yīng)的一系列配置項。
- mail: 實現(xiàn) email 相關(guān)的 SMTP/IMAP/POP3 代理時,共享的一些配置項。
模塊化體系結(jié)構(gòu)
模塊化體系結(jié)構(gòu)
Nginx 的內(nèi)部結(jié)構(gòu)是由核心部分和一系列的功能模塊所組成。這樣劃分是為了使得每個模塊的功能相對簡單,便于開發(fā),同時也便于對系統(tǒng)進(jìn)行功能擴展。
Nginx 提供了 Web 服務(wù)器的基礎(chǔ)功能,也提供了 Web 服務(wù)反向代理,Email 服務(wù)反向代理功能。Nginx 的核心功能部分實現(xiàn)了底層的通訊協(xié)議,為其他模塊和 Nginx 進(jìn)程構(gòu)建了基本的運行時環(huán)境,并且構(gòu)建了其他各模塊的協(xié)作基礎(chǔ)。
模塊的分類
Nginx 的模塊根據(jù)其功能基本上可以分為以下幾種類型:
- event module: 搭建了獨立于操作系統(tǒng)的事件處理機制的框架,及提供了各具體事件的處理。包括 ngx_events_module, ngx_event_core_module 和 ngx_epoll_module 等。Nginx 具體使用何種事件處理模塊,依賴于具體的操作系統(tǒng)和編譯選項。
- phase handler: 此類型的模塊也被直接稱為 handler 模塊。主要負(fù)責(zé)處理客戶端請求并產(chǎn)生待響應(yīng)內(nèi)容,比如 ngx_http_static_module 模塊,負(fù)責(zé)客戶端的靜態(tài)頁面請求處理并將對應(yīng)的磁盤文件準(zhǔn)備為響應(yīng)內(nèi)容輸出。
- output filter: 也稱為 filter 模塊,主要是負(fù)責(zé)對輸出的內(nèi)容進(jìn)行處理,可以對輸出進(jìn)行修改。例如,可以實現(xiàn)對輸出的所有 html 頁面增加預(yù)定義的 footbar 一類的工作,或者對輸出的圖片的 URL 進(jìn)行替換之類的工作。
- upstream: upstream 模塊實現(xiàn)反向代理的功能,將真正的請求轉(zhuǎn)發(fā)到后端服務(wù)器上,并從后端服務(wù)器上讀取響應(yīng),發(fā)回客戶端。upstream 模塊是一種特殊的 handler,只不過響應(yīng)內(nèi)容不是真正由自己產(chǎn)生的,而是從后端服務(wù)器上讀取的。
- load-balancer: 負(fù)載均衡模塊,實現(xiàn)特定的算法,在眾多的后端服務(wù)器中,選擇一個服務(wù)器出來作為某個請求的轉(zhuǎn)發(fā)服務(wù)器。
請求處理
請求的處理流程
實際上業(yè)務(wù)處理邏輯都在 worker 進(jìn)程中。worker 進(jìn)程中有一個函數(shù),執(zhí)行無限循環(huán),不斷處理收到的來自客戶端的請求,并進(jìn)行處理,直到整個 Nginx 服務(wù)被停止。
worker 進(jìn)程中,ngx_worker_process_cycle()函數(shù)就是處理函數(shù)。在這個函數(shù)中,一個請求的簡單處理流程如下:
- 操作系統(tǒng)提供的機制(例如 epoll, kqueue 等)產(chǎn)生相關(guān)的事件。
- 接收和處理這些事件,如接受到數(shù)據(jù),則產(chǎn)生更高層的 request 對象。
- 處理 request 的 header 和 body。
- 產(chǎn)生響應(yīng),并發(fā)送回客戶端。
- 完成 request 的處理。
- 重新初始化定時器及其他事件。
總結(jié)
以上是生活随笔為你收集整理的Nginx学习笔记(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装Jenkins及配置自由风格自动打包
- 下一篇: Nginx学习笔记(二)