日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《深入理解NGINX 模块开发与架构解析》之摘抄学习

發(fā)布時(shí)間:2024/1/23 编程问答 61 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《深入理解NGINX 模块开发与架构解析》之摘抄学习 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1.基于Nginx框架開發(fā)程序有5個(gè)優(yōu)勢(shì):

? ? (1).Nginx將網(wǎng)絡(luò)、磁盤及定時(shí)器等異步事件的驅(qū)動(dòng)都做了非常好的封裝,基于它開發(fā)將可以忽略這些事件處理的細(xì)節(jié);

? ? (2).Nginx封裝了許多平臺(tái)無關(guān)的接口、容器,適用于跨平臺(tái)開發(fā)。

? ? (3) 優(yōu)秀的模塊化設(shè)計(jì),使得開發(fā)者可以輕易地復(fù)用各種已有的模塊,其中既包括基本的讀取配置、記錄日志等模塊,也包括處理請(qǐng)求的諸如HTTP.mail等高級(jí)功能模塊;

? ? (4)?Nginx是作為服務(wù)器來設(shè)計(jì)其框架的,因此,它在服務(wù)器進(jìn)程的管理上相當(dāng)出色,基于它開發(fā)服務(wù)器程序可以輕松地實(shí)現(xiàn)程序的動(dòng)態(tài)升級(jí),子進(jìn)程的監(jiān)控、管理,配置項(xiàng)的動(dòng)態(tài)修改生效等;

? ? (5).Nginx充分考慮到各操作系統(tǒng)所擅長的“絕活”,能夠使用特殊的系統(tǒng)調(diào)用更高效地完成任務(wù)時(shí),絕不會(huì)去使用低效的通用接口。尤其對(duì)于Linux操作系統(tǒng),Nginx不遺余力地做了大量優(yōu)化。

2.由于默認(rèn)的Linux內(nèi)核參數(shù)考慮的是最通用的場(chǎng)景,這明顯不符合用于支持高并發(fā)訪問的Web服務(wù)器的定義,所以需要修改Linux內(nèi)核參數(shù),使得Nginx可以擁有更高的性能。

? ?最常用的配置:

fs.file-max = 999999 //這個(gè)參數(shù)表示進(jìn)程(比如一個(gè)worker進(jìn)程)可以同時(shí)打開的最大句柄數(shù) net.ipv4.tcp_tw_reuse = 1 //這個(gè)參數(shù)設(shè)置為1,表示允許將TIME-WAIT狀態(tài)的socket重新用于新的TCP連接,這對(duì)于服務(wù)器來說很有意義,因?yàn)榉?wù)器上總會(huì)有大量TIME-WAIT狀態(tài)的連接。 net.ipv4.tcp_keepalive_time = 600 //這個(gè)參數(shù)表示當(dāng)前keepalive啟用時(shí),TCP發(fā)送keepalive消息的頻度。默認(rèn)是2小時(shí),若將其設(shè)置得小一些,可以更快地清理無效的連接 net.ipv4.tcp_fin_timeout = 30 //這個(gè)參數(shù)表示當(dāng)前當(dāng)服務(wù)器主動(dòng)關(guān)閉連接時(shí),socket保持在FIN-WAIT-2狀態(tài)的最大時(shí)間 net.ipv4.tcp_max_tw_buckets = 5000 //這個(gè)參數(shù)表示操作系統(tǒng)允許TIME_WAIT套接字?jǐn)?shù)量的最大值,如果超過這個(gè)數(shù)字,TIME_WAIT套接字將立刻被清除并打印警告信息。 net.ipv4.ip_local_port_range = 1024 61000 //這個(gè)參數(shù)定義了在UDP和TCP連接中本地(不包括連接的遠(yuǎn)端)端口的取值范圍。 net.ipv4.tcp_rmem = 4096 32768 262142 //這個(gè)參數(shù)定義了TCP接收緩存(用于TCP接受滑動(dòng)窗口)的最小值、默認(rèn)值、最大值。 net.ipv4.tcp_wmem = 4096 32768 262142 //這個(gè)參數(shù)定義了TCP發(fā)送緩存(用于TCP發(fā)送滑動(dòng)窗口)的最小值、默認(rèn)值、最大值。 net.core.netdev_max_backlog = 8096 //當(dāng)網(wǎng)卡接收數(shù)據(jù)包的速度大于內(nèi)核處理的速度時(shí),會(huì)有一個(gè)隊(duì)列保存這些數(shù)據(jù)包。這個(gè)參數(shù)表示該隊(duì)列的最大值。 net.core.rmem_default = 262144 //這個(gè)參數(shù)表示內(nèi)核套接字接收緩存區(qū)默認(rèn)的大小。 net.core.wmem_default = 262144 //這個(gè)參數(shù)表示內(nèi)核套接字發(fā)送緩存區(qū)默認(rèn)的大小。 net.core.rmem_max = 2097152 //這個(gè)參數(shù)表示內(nèi)核套接字接收緩存區(qū)的最大大小。 net.core.wmem_max = 2097152 //這個(gè)參數(shù)表示內(nèi)核套接字發(fā)送緩存區(qū)的最大大小。 net.ipv4.tcp_syncookies = 1 //該參數(shù)與性能無關(guān),用于解決TCP的SYN攻擊。 net.ipv4.tcp_max_syn_backlog = 1024 //這個(gè)參數(shù)表示TCP三次握手建立階段SYN請(qǐng)求隊(duì)列的最大長度,默認(rèn)為1024,將其設(shè)置得大一些可以使出現(xiàn)Nginx繁忙來不及accept新連接的情況時(shí),Linux不至于丟失客戶端發(fā)起的連接請(qǐng)求。

3.configure腳本的內(nèi)容如下:

#!bin/sh# Copyright (C) Igor Sysoev # Copyright (C) Nginx, Inc.#auto/options腳本處理configure命令的參數(shù)。例如,如果參數(shù)是--help,那么顯示支持的所有參數(shù)格式。options腳本會(huì)定義后續(xù)工作將要用到的變量,然后根據(jù)本次參數(shù)以及默認(rèn)值設(shè)置這些變量 . auto/options#auto/init腳本初始化后續(xù)將產(chǎn)生的文件路徑。例如,Makefile、ngx_modules.c等文件默認(rèn)情況下會(huì)在<nginx-source>/objs/ . auto/init#auto/sources腳本將分析Nginx的源碼結(jié)構(gòu),這樣才能構(gòu)造后續(xù)的Makefile文件 . auto/sources# 編譯過程中所有目錄文件生成的路徑由--builddir=DIR參數(shù)指定,默認(rèn)情況下為<nginx-source>/objs,此時(shí)這個(gè)目錄將會(huì)被創(chuàng)建 test -d $NGX_OBJS || mkdir $NGX_OBJS# 開始準(zhǔn)備建立ngx_auto_headers.h、autoconf.err等必要的編譯文件 echo > $NGX_AUTO_HEADERS_H echo > $NGX_AUTOCONF_ERR# 向objs/ngx_auto_config.h寫入命令行帶的參數(shù) echo "#define NGX_CONFIGURE \"$NGX_CONFIGURE\"" > $NGX_AUTO_CONFIG_H# 判斷DEBUG標(biāo)志,如果有,那么在objs/ngx_auto_config.h文件中寫入DEBUG宏 if [ $NGX_DEBUG = YES ]; thenhave=NGX_DEBUG . auto/have fi# 現(xiàn)在開始檢查操作系統(tǒng)參數(shù)是否支持后續(xù)編譯 if test -z "$NGX_PLATFORM"; thenecho "checking for OS"NGX_SYSTEM=`uname -s 2>/dev/null`NGX_RELEASE=`uname -r 2>/dev/null`NGX_MACHINE=`uname -m 2>/dev/null`#屏幕上輸出OS名稱、內(nèi)核版本、32位/64位內(nèi)核echo " + $NGX_SYSTEM $NGX_RELEASE $NGX_MACHINE"NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE";case "$NGX_SYSTEM" inMINGW32_*)NGX_PLATFORM=win32;;esacelseecho "building for $NGX_PLATFORM"NGX_SYSTEM=$NGX_PLATFORMfi#檢查并設(shè)置編譯器,如GCC是否安裝、GCC版本是否支持后續(xù)編譯nginx. auto/cc/conf# 對(duì)非Windows操作系統(tǒng)定義一些必要的頭文件,并檢查其是否存在,一次決定configure后續(xù)步驟是否可以成功if [ "$NGX_PLATFORM" != win32 ]; then. auto/headersfi# 對(duì)于當(dāng)前操作系統(tǒng),定義一些特定的操作系統(tǒng)相關(guān)的方法并檢查當(dāng)前環(huán)境是否支持。例如,對(duì)于Linux,在這里使用sched_sestaffinity設(shè)置進(jìn)程優(yōu)先級(jí),使用Linux特有的sendfile系統(tǒng)調(diào)用來加速向網(wǎng)絡(luò)中發(fā)送文件塊. auto/os/conf# 定義類UNIX操作系統(tǒng)中通用的頭文件和系統(tǒng)調(diào)用等,并檢查當(dāng)前環(huán)境是否支持if [ "$NGX_PLATFORM" != win32 ]; then. auto/unixfi#最核心的構(gòu)造運(yùn)行期modules的腳本。它將會(huì)生成ngx_modules.c文件,這個(gè)文件會(huì)被編譯進(jìn)Nginx中,其中它所做的唯一的事情就是定義了ngx_modules數(shù)組。ngx_modules指明Nginx運(yùn)行期間有哪些模塊會(huì)參與到請(qǐng)求的處理中,包括HTTP請(qǐng)求可能會(huì)使用哪些模塊處理,因此,它對(duì)數(shù)組元素的順序非常敏感,也就是說,絕大部分模塊在ngx_modules數(shù)組中的順序其實(shí)是固定的。例如,一個(gè)請(qǐng)求必須先執(zhí)行ngx_http_gzip_filter_module模塊重新修改HTTP響應(yīng)中的頭部后,才能使用ngx_http_header_filter模塊按照headers_in結(jié)構(gòu)體里的成員構(gòu)造出以TCP流形式發(fā)送給客戶端的HTTP響應(yīng)頭部。注意,我們?cè)?-add-module=參數(shù)里加入的第三方模塊也在此步驟寫入到ngx_modules.c文件中了. auto/modules# conf腳本用來檢查Nginx在鏈接期間需要鏈接的第三方靜態(tài)庫、動(dòng)態(tài)庫或者目標(biāo)文件是否存在. auto/lib/conf# 處理Nginx安裝后的路徑case ".$NGX_PREFIX" in.)NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx}have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define;;.!)NGX_PREFIX=;;*)have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define;;esac# 處理Nginx安裝后conf文件的路徑if [ ".$NGX_CONF_PREFIX" != "." ]; thenhave=NGX_CONF_PREFIX value="\"$NGX_CONF_PREFIX/\"" . auto/definefi# 處理Nginx安裝后,二進(jìn)制文件、pid、lock等其他文件的路徑可參見configure參數(shù)中路徑類選項(xiàng)的說明have=NGX_SBIN_PATH value="\"$NGX_SBIN_PATH\"" . auto/definehave=NGX_CONF_PATH value="\"$NGX_CONF_PATH\"" . auto/definehave=NGX_PID_PATH value="\"$NGX_PID_PATH\"" . auto/definehave=NGX_LOCK_PATH value="\"$NGX_LOCK_PATH\"" . auto/definehave=NGX_ERROR_LOG_PATH value="\"$NGX_ERROR_LOG_PATH\"" . auto/definehave=NGX_HTTP_LOG_PATH value="\"$NGX_HTTP_LOG_PATH\"" . auto/definehave=NGX_HTTP_CLIENT_TEMP_PATH value="\"$NGX_HTTP_CLIENT_TEMP_PATH\"" . auto/definehave=NGX_HTTP_PROXY_TEMP_PATH value="\"$NGX_HTTP_PROXY_TEMP_PATH\"" . auto/definehave=NGX_HTTP_FASTCGI_TEMP_PATH value="\"$NGX_HTTP_FASTCGI_TEMP_PATH\"" . auto/definehave=NGX_HTTP_UWSGI_TEMP_PATH value="\"$NGX_HTTP_UWSGI_TEMP_PATH\"" . auto/definehave=NGX_HTTP_SCGI_TEMP_PATH value="\"$NGX_HTTP_SCGI_TEMP_PATH\"" . auto/define# 創(chuàng)建編譯時(shí)使用的objs/Makefile文件. auto/make# 為objs/Makefile加入需要連接的第三方靜態(tài)庫、動(dòng)態(tài)庫或者目標(biāo)文件. auto/lib/make# 為objs/Makefile加入install功能,當(dāng)執(zhí)行make install時(shí)將編譯生成的必要文件復(fù)制到安裝路徑,建立必要的目錄. auto/install# 在ngx_auto_config.h文件中加入NGX_SUPPERSS_WARN宏、NGX_SMP宏. auto/stubs# 在ngx_auto_config.h文件中指定NGX_USER和NGX_GROUP宏,如果執(zhí)行configure時(shí)沒有參數(shù)指定,默認(rèn)兩者皆為nobody(也就是默認(rèn)以nobody用戶運(yùn)行進(jìn)程)have=NGX_USER value="\"$NGX_USER\"" . auto/definehave=NGX_GROUP value="\"$NGX_GROUP\"" . auto/define# 顯示configure執(zhí)行的結(jié)果,如果失敗,則給出原因. auto/summary

4.ngx_moduels.c文件就是用來定義ngx_moduels數(shù)組的。它指明了每個(gè)模塊在Nginx中的優(yōu)先級(jí),當(dāng)一個(gè)請(qǐng)求同時(shí)符合多個(gè)模塊的處理規(guī)則時(shí),將按照它們?cè)趎gx_moduels數(shù)組中的順序選擇最靠前的模塊優(yōu)先處理。對(duì)于HTTP過濾模塊而言,在ngx_modules數(shù)組中越是靠后的模塊反而會(huì)首先處理HTTP響應(yīng)。

5.日志文件回滾

? ?使用-s reopen參數(shù)可以重新打開日志文件,這樣可以先把當(dāng)前日志文件改名或轉(zhuǎn)移到其他目錄中進(jìn)行備份,再重新打開時(shí)就會(huì)生成新的日志文件。這個(gè)功能使得日志文件不至于過大。

6.平滑升級(jí)Nginx??

? ?當(dāng)Nginx服務(wù)升級(jí)到新的版本時(shí),必須要將舊的二進(jìn)制文件Nginx替換掉,通常情況下這是需要重啟服務(wù)的,但Nginx支持不重啟服務(wù)來完成新版本的平滑升級(jí)。

? ?升級(jí)時(shí)包括以下步驟:

? ? 1) 通知正在運(yùn)行的舊版本Nginx準(zhǔn)備升級(jí)。通過向master進(jìn)程發(fā)送USR2信號(hào)可達(dá)到目的。例如:

kill -s SIGUSR2 <nginx master pid>

? ? ? 這時(shí),運(yùn)行中的Nginx會(huì)將pid文件重命名,如將/usr/local/nginx/los/nginx.pid重命名為/usr/local/nginx/logs/nginx.pid.oldbin,這樣新的Nginx才有可能啟動(dòng)成功。

? ? ?2) 啟動(dòng)新版本的Nginx,可以使用以上介紹過的任意一種啟動(dòng)方法,這時(shí)通過ps命令可以發(fā)現(xiàn)新舊版本的Nginx在同時(shí)運(yùn)行。

? ? ?3) 通過kill命令向舊版本的master進(jìn)程發(fā)送SIGQUIT信號(hào),以“優(yōu)雅”的方式關(guān)閉舊版本的Nginx。隨后將只有新版本的Nginx服務(wù)運(yùn)行,此時(shí)平滑升級(jí)完畢。

7.部署后Nginx進(jìn)程間的關(guān)系

? ?

8.系統(tǒng)調(diào)用gettimeofday的執(zhí)行頻率

? ?默認(rèn)情況下,每次內(nèi)核的事件調(diào)用(如epoll、select、poll、kqueue等)返回時(shí),都會(huì)執(zhí)行一次gettimeofdata,實(shí)現(xiàn)用內(nèi)核的時(shí)鐘來更新Nginx中的緩存時(shí)鐘。

9.server_name后可以跟多個(gè)主機(jī)名稱,如server_name www.testweb.com、download.testweb.com;。

? 在開始處理一個(gè)HTTP請(qǐng)求時(shí),Nginx會(huì)取出header頭中的Host,與每個(gè)server中的server_name進(jìn)行匹配,以此決定到底由哪一個(gè)server塊來處理這個(gè)請(qǐng)求。有可能一個(gè)Host與多個(gè)server塊中的server_name都匹配,這是就會(huì)根據(jù)匹配優(yōu)先級(jí)來選擇實(shí)際處理的server塊。server_name與Host的匹配優(yōu)先級(jí)如下:

? ? 1) 首先選擇所有字符串完全匹配的server_name,如www.testweb.com.

? ? 2)其次選擇通配符在前面的server_name,如 *.testweb.com。

? ? 3)再次選擇通配符在后面的server_name,如www.testweb.*。

? ? 4)最后選擇使用正則表達(dá)式才匹配的server_name,如~^\.testweb\.com$。

10.作為靜態(tài)Web服務(wù)器與反向代理服務(wù)器的Nginx

? ??

11.Nginx作為反向代理服務(wù)器時(shí)轉(zhuǎn)發(fā)請(qǐng)求的流程

? ??

? ?當(dāng)客戶端發(fā)來HTTP請(qǐng)求時(shí),Nginx并不會(huì)立刻轉(zhuǎn)發(fā)到上游服務(wù)器,而是先把用戶的請(qǐng)求(包括HTTP包體)完整地接收到Nginx所在服務(wù)器的硬盤或者內(nèi)存中,然后再向上游服務(wù)器發(fā)起連接,把緩存的客戶端請(qǐng)求轉(zhuǎn)發(fā)到上游服務(wù)器。而Squid等代理服務(wù)器則采用一邊接收客戶端請(qǐng)求,一邊轉(zhuǎn)發(fā)到上游服務(wù)器的方式。

? ?Nginx的這種工作方式有什么優(yōu)缺點(diǎn)呢?很明顯,缺點(diǎn)是延長了一個(gè)請(qǐng)求的處理時(shí)間,并增加了用于緩存請(qǐng)求內(nèi)容的內(nèi)存內(nèi)核磁盤空間。而優(yōu)點(diǎn)則是降低了上游服務(wù)器的負(fù)載,盡量把壓力放在Nginx服務(wù)器上。

12.Nginx HTTP模塊調(diào)用的簡(jiǎn)化流程

? ??

13.在Linux平臺(tái)下,Nginx對(duì)ngx_int_t和ngx_uint_t的定義如下:

typedef intptr_t ngx_int_t; typedef uintptr_t ngx_uint_t;

14.ngx_str_t的定義如下:

typedef struct {size_t len;u_char *data; } ngx_str_t;

? ?任何視圖將ngx_str_t的data成員當(dāng)做字符串來使用的情況,都可能導(dǎo)致內(nèi)存越界!

? ?Nginx使用ngx_str_t可以有效地降低內(nèi)存使用量。例如,用戶請(qǐng)求“GET /test?a=1 http/1.1\r\n”存儲(chǔ)到內(nèi)存地址0x1d0b0110上,這時(shí)只需要把r->method_name設(shè)置為{len = 3,data = 0x1d0b0110}就可以表示方法名"GET",而不需要單獨(dú)為method_name再分配內(nèi)存冗余的存儲(chǔ)字符串。

15.ngx_list_t是Nginx封裝的鏈表容器,它在Nginx中使用得很頻繁,例如HTTP的頭部就是用ngx_list_t來存儲(chǔ)的。先看一下ngx_list_t相關(guān)成員的定義:

typedef struct ngx_list_part_s ngx_list_part_t;struct ngx_list_part_s {void *elts; //指向數(shù)組的起始地址。ngx_uint_t nelts; //表示數(shù)組中已經(jīng)使用了多少個(gè)元素,當(dāng)然,nelts必須小于ngx_list_t結(jié)構(gòu)體中的nalloc.ngx_list_part_t *next; //下一個(gè)鏈表元素ngx_list_part_t的地址。 };typedef struct {ngx_list_part_t *last; //指向鏈表的最后一個(gè)數(shù)組元素ngx_list_part_t part; //鏈表的首個(gè)數(shù)組元素size_t size; //鏈表中的每個(gè)ngx_list_part_t元素都是一個(gè)數(shù)組。因?yàn)閿?shù)組存儲(chǔ)的是某種類型的數(shù)據(jù)結(jié)構(gòu),且ngx_list_t是非常靈活的數(shù)據(jù)結(jié)構(gòu),所以它不會(huì)限制存儲(chǔ)什么樣的數(shù)據(jù),只是通過size限制每一個(gè)數(shù)組元素的占用的空間大小,也就是用戶要存儲(chǔ)的一個(gè)數(shù)據(jù)所占用的字節(jié)數(shù)必須小于或等于size。ngx_uint_t nalloc; //鏈表的數(shù)組元素一旦分配后是不可更改的。nalloc表示每個(gè)ngx_list_part_t數(shù)組的容量,即最多可存儲(chǔ)多少個(gè)數(shù)據(jù)。ngx_pool_t *pool; //鏈表中管理內(nèi)存分配的內(nèi)存池對(duì)象。用戶要存放的數(shù)據(jù)占用的內(nèi)存都是由pool分配的。 } ngx_list_t;

? ngx_list_t的內(nèi)存分布情況如下:

? ?

? ?上圖中是由3個(gè)ngx_list_part_t數(shù)組元素組成的ngx_list_t鏈表中可能擁有的一種內(nèi)存分布結(jié)構(gòu)。這里,pool內(nèi)存池為其分配了連續(xù)的內(nèi)存,最前端內(nèi)存存儲(chǔ)的是ngx_list_t結(jié)構(gòu)中的成員,緊接著是第一個(gè)ngx_list_part_t結(jié)構(gòu)占用的內(nèi)存,然后是ngx_list_part_t結(jié)構(gòu)指向的數(shù)組,它們一共占用size*nalloc字節(jié),表示數(shù)組中擁有nalloc個(gè)大小為size的元素。其后面是第2個(gè)ngx_list_part_t結(jié)構(gòu)以及它所指向的數(shù)組,依次類推。

16.ngx_table_elt_t數(shù)據(jù)結(jié)構(gòu)如下所示:

typedef struct {ngx_uint_t hash; //表明ngx_table_elt_t也可以是某個(gè)散列表數(shù)據(jù)結(jié)構(gòu)(ngx_hash_t類型)中的成員ngx_str_t key;ngx_str_t value;u_char *lowcase_key; } ngx_table_elt_t;

? 顯而易見,ngx_table_elt_t是為HTTP頭部"量身定制"的。

17.緩沖區(qū)ngx_buf_t是Nginx處理大數(shù)據(jù)的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),它既應(yīng)用于內(nèi)存數(shù)據(jù)也應(yīng)用于磁盤數(shù)據(jù)。下面主要介紹ngx_buf_t結(jié)構(gòu)體本身:

typedef struct ngx_buf_s ngx_buf_t; typedef void * ngx_buf_tag_t; struct ngx_buf_s {/* pos通常是用來告訴使用者本次應(yīng)該從pos這個(gè)位置開始處理內(nèi)存中的數(shù)據(jù),這樣設(shè)置是因?yàn)橥粋€(gè)ngx_buf_t可能被多次反復(fù)處理,當(dāng)然,pos的含義是由使用它的模塊定義的*/u_char *pos;/* last通常表示有效的內(nèi)容到此為止,注意,pos與last之間的內(nèi)存是希望nginx處理的內(nèi)容*/u_char *last;/*處理文件時(shí),file_pos與file_last的含義與處理內(nèi)存時(shí)的pos與last相同,file_pos表示將要處理的文件位置,file_last表示截止的文件位置*/off_t file_pos;off_t file_last;//如果ngx_buf_t緩沖區(qū)用于內(nèi)存,那么start指向這段內(nèi)存的起始地址u_char *start;//與start成員對(duì)應(yīng),指向緩沖區(qū)內(nèi)存的末尾u_char *end;/* 表示當(dāng)前緩沖區(qū)的類型,例如由哪個(gè)模塊使用就指向這個(gè)模塊ngx_module_t變量的地址*/ngx_buf_tag_t tag;//引用的文件ngx_file_t *file;/* 當(dāng)前緩沖區(qū)的影子緩沖區(qū),該成員很少用到,僅僅在12.8節(jié)描述的使用緩沖區(qū)轉(zhuǎn)發(fā)上游服務(wù)器的響應(yīng)時(shí)才使用了shadow成員,這是因?yàn)镹ginx太節(jié)約內(nèi)存了,分配一塊內(nèi)存并使用ngx_buf_t表示接收到的上游服務(wù)器響應(yīng)后,在向下游客戶端轉(zhuǎn)發(fā)時(shí)可能會(huì)把這塊內(nèi)存存儲(chǔ)到文件中,也可能直接向下游發(fā)送,此時(shí)Nginx絕不會(huì)重新復(fù)制一份內(nèi)存用于新的目的,而是再次建立一個(gè)ngx_buf_t結(jié)構(gòu)體指向原內(nèi)存,這樣多個(gè)ngx_buf_t結(jié)構(gòu)體指向了同一塊內(nèi)存,它們之間的關(guān)系就通過shadow成員來引用。這種設(shè)計(jì)過于復(fù)雜,通常不建議使用*/ngx_buf_t *shadow;//臨時(shí)內(nèi)存標(biāo)志位,為1時(shí)表示數(shù)據(jù)在內(nèi)存中且這段內(nèi)存可以修改unsigned temporary:1;//標(biāo)志位,為1時(shí)表示數(shù)據(jù)在內(nèi)存中且這段內(nèi)存不可以被修改unsigned memory:1;//標(biāo)志位,為1時(shí)表示這段內(nèi)存使用mmap系統(tǒng)調(diào)用映射過來的,不可以被修改unsigned mmap:1;//標(biāo)志位,為1時(shí)表示可回收unsigned recycled:1;//標(biāo)志位,為1時(shí)表示這段緩沖區(qū)處理的是文件而不是內(nèi)存unsigned in_file:1;//標(biāo)志位,為1時(shí)表示需要執(zhí)行flush操作unsigned flush:1;/*標(biāo)志位,對(duì)于操作這塊緩沖區(qū)時(shí)是否使用同步方式,需謹(jǐn)慎考慮,這可能會(huì)阻塞Nginx進(jìn)程,Nginx中所有操作幾乎都是異步的,這是它支持高并發(fā)的關(guān)鍵。有些框架代碼在sync為1時(shí)可能會(huì)有阻塞的方式進(jìn)行I/O操作,它的意義視使用它的Nginx模塊而定*/unsigned sync:1;/*標(biāo)志位,表示是否是最后一塊緩沖區(qū),因?yàn)閚gx_buf_t可以由ngx_chain_t鏈表串聯(lián)起來,因?yàn)?#xff0c;當(dāng)last_buf為1時(shí),表示當(dāng)前是最后一塊待處理的緩沖區(qū)*/unsigned last_buf:1;//標(biāo)志位,表示是否是ngx_chain-t中的最后一塊緩沖區(qū)unsigned last_in_chain:1;/* 標(biāo)志位,表示是否是最后一個(gè)影子緩沖區(qū),與shadow域配合使用。通常不建議使用它*/unsigned last_shadow:1;//標(biāo)志位,表示當(dāng)前緩沖區(qū)是否屬于臨時(shí)文件unsigned temp_file:1; };

18.ngx_chain_t是與ngx_buf_t配合使用的鏈表數(shù)據(jù)結(jié)構(gòu),來看一下定義:

typedef struct ngx_chain_s ngx_chain_t; struct ngx_chain_s {ngx_buf_t *buf; //指向當(dāng)前的ngx_buf_t緩沖區(qū)ngx_chain-t *next; //用來指向下一個(gè)ngx_chain_t,如果這是最后一個(gè)ngx_chain_t,則需要把next置為NULL。 };

? 在向用戶發(fā)送HTTP包體時(shí),就要傳入ngx_chain_t鏈表對(duì)象,注意,如果是最后一個(gè)ngx_chain_t,那么必須將next置為NULL,否則永遠(yuǎn)不會(huì)發(fā)送成功,而且這個(gè)請(qǐng)求將一直不會(huì)結(jié)束(Nginx框架的要求).

19.ngx_module_t是一個(gè)Nginx模塊的數(shù)據(jù)結(jié)構(gòu),如下所示:

typedef struct ngx_module_s ngx_module_t; struct ngx_module_s {/* 下面的ctx_index、index、spare0、spare1、spare2、spare3、version變量不需要在定義時(shí)賦值,可以用Nginx準(zhǔn)備好的宏NGX_MODULE_V1來定義,它已經(jīng)定義好了這7個(gè)值 #define NGX_MODULE_V1 0,0,0,0,0,0,1對(duì)于一類模塊(由下面的type成員決定類別)而言,ctx_index表示當(dāng)前模塊在這類模塊中的序號(hào)。這個(gè)成員常常是由管理這類模塊的一個(gè)Nginx核心模塊設(shè)置的,對(duì)于所有的HTTP模塊而言,ctx_index是由核心模塊ngx_http_module設(shè)置的。ctx_index非常重要,Nginx的模塊化設(shè)計(jì)非常依賴于各個(gè)模塊的順序,它們既用于表達(dá)優(yōu)先級(jí),也用于表明每個(gè)模塊的位置,借以幫助Nginx框架快速獲得某個(gè)模塊的數(shù)據(jù)*/ngx_uint_t ctx_index;/*index表示當(dāng)前模塊在ngx_modules數(shù)組中的序號(hào),注意,ctx_index表示的是當(dāng)前模塊在一類模塊中的序號(hào),而index表示當(dāng)前模塊在所有模塊中的序號(hào),它同樣關(guān)鍵。Nginx啟動(dòng)時(shí)會(huì)根據(jù)ngx_modules數(shù)組設(shè)置各模塊的index值,例如:ngx_max_module = 0;for (i=0; ngx_modules[i]; i++) {ngx_modules[i]->index = ngx_max_module++;}*/ngx_uint_t index;//spare系列的保留變量,暫未使用ngx_uint_t spare0;ngx_uint_t spare1;ngx_uint_t spare2;ngx_uint_t spare3;//模塊的版本,便于將來的擴(kuò)展。目前只有一種,默認(rèn)為1ngx_uint_t version;/*ctx用于指向一類模塊的上下文結(jié)構(gòu)體,為什么需要ctx呢?因?yàn)榍懊嬲f過,Nginx模塊有許多種類,不同類模塊之間的功能差別很大。例如,事件類型的模塊主要處理I/O事件相關(guān)的功能,HTTP類型的模塊主要處理HTTP應(yīng)用層的功能。這樣,每個(gè)模塊都有了自己的特性,而ctx將會(huì)指向特定類型模塊的公共接口。例如,在HTTP模塊中,ctx需要指向ngx_http_module_t結(jié)構(gòu)體*/void *ctx;//commands將處理nginx.conf中的配置項(xiàng)ngx_command_t *commands;/*type表示該模型的類型,它與ctx指針是緊密相關(guān)的。在官方Nginx中,它的取值范圍是以下5種:NGX_HTTP_MODULE、NGX_CORE_MODULE、NGX_CONF_MODULE、NGX_EVENT_MODULE、NGX_MAIL_MODULE。*/ngx_uint_t type;/*在Nginx的啟動(dòng)、停止過程中,以下7個(gè)函數(shù)指針表示有7個(gè)執(zhí)行點(diǎn)會(huì)分別調(diào)用者7種方法。對(duì)于任一個(gè)方法而言,如果不需要Nginx在某個(gè)時(shí)刻執(zhí)行它,那么簡(jiǎn)單地把它設(shè)為NULL空指針即可*//*雖然從字面上理解應(yīng)當(dāng)在master進(jìn)程啟動(dòng)時(shí)回調(diào)init_master,但到目前為止,框架代碼從來不會(huì)調(diào)用它,因此,可將init_master設(shè)為NULL */ngx_int_t (*init_master)(ngx_lot_t *log);/* init_module回調(diào)方法在初始化所有模塊時(shí)被調(diào)用。在master/worker模式下,這個(gè)階段將在啟動(dòng)worker子進(jìn)程前完成*/ngx_int_t (*init_module)(ngx_cycle_t *cycle);/*init_process回調(diào)方法在正常服務(wù)前被調(diào)用。在master/worker模式下,多個(gè)worker子進(jìn)程已經(jīng)產(chǎn)生,在每個(gè)worker進(jìn)程的初始化過程會(huì)調(diào)用所有模塊的init_process函數(shù) */ngx_int_t (*init_process)(ngx_cycle_t *cycle);/*由于Nginx暫不支持多線程模式,所以init_thread在框架代碼中沒有被調(diào)用過,設(shè)為NULL*/ngx_int_t (*init_thread)(ngx_cycle_t *cycle);//同上,exit_thread也不支持,設(shè)為NULL.void (*exit_process)(ngx_cycle_t *cycle);//exit_master回調(diào)方法將在master進(jìn)程退出前被調(diào)用void (*exit_master)(ngx_cycle_t *cycle);/*保留字段,目前沒有使用*/uintptr_t spare_hook0;uintptr_t spare_hook1;uintptr_t spare_hook2;uintptr_t spare_hook3;uintptr_t spare_hook4;uintptr_t spare_hook5;uintptr_t spare_hook6;uintptr_t spare_hook7; };

20.HTTP框架在讀取、重載配置文件時(shí)定義了由ngx_http_module_t接口描述的8個(gè)階段,HTTP框架在啟動(dòng)過程中會(huì)在每一個(gè)階段中調(diào)用ngx_http_module_t中相應(yīng)的方法。

typedef struct {//解析配置文件前調(diào)用ngx_int_t (*preconfiguration)(ngx_conf_t *cf);//完成配置文件的解析后調(diào)用ngx_int_t (*postconfiguration)(ngx_conf_t *cf);/*當(dāng)需要?jiǎng)?chuàng)建數(shù)據(jù)結(jié)構(gòu)用于存儲(chǔ)main級(jí)別(直屬于http{...}塊的配置項(xiàng))的全局配置項(xiàng)時(shí),可以通過create_main_conf回調(diào)方法創(chuàng)建存儲(chǔ)全局配置項(xiàng)的結(jié)構(gòu)體 */void *(*create_main_conf)(ngx_conf_t *cf);//常用于初始化main級(jí)別配置項(xiàng)char *(*init_main_conf)(ngx_conf_t *cf, void *conf);/* 當(dāng)需要?jiǎng)?chuàng)建數(shù)據(jù)結(jié)構(gòu)用于存儲(chǔ)srv級(jí)別(直屬于虛擬主機(jī)server{...}塊的配置項(xiàng))的配置項(xiàng)時(shí),可以通過實(shí)現(xiàn)create_srv_conf回調(diào)方法創(chuàng)建存儲(chǔ)srv級(jí)別配置項(xiàng)的結(jié)構(gòu)體 */void *(*create_srv_conf)(ngx_conf_t *cf);//merge_srv_conf回調(diào)方法主要用于合并main級(jí)別和srv級(jí)別下的同名配置項(xiàng)char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);/*當(dāng)需要?jiǎng)?chuàng)建數(shù)據(jù)結(jié)構(gòu)用于存儲(chǔ)loc級(jí)別(直屬于location{...}塊的配置項(xiàng))的配置項(xiàng)時(shí),可以實(shí)現(xiàn)create_loc_conf回調(diào)方法*/void *(*create_loc_conf)(ngx_conf_t *cf);//merge_loc_conf回調(diào)方法主要用于合并srv級(jí)別和loc級(jí)別下的同名配置項(xiàng)char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); } ngx_http_module_t;

21.每一個(gè)ngx_command_t結(jié)構(gòu)體定義了自己感興趣的一個(gè)配置項(xiàng):

typedef struct ngx_command_s ngx_command_t; struct ngx_command_s {//配置項(xiàng)名稱,如"gzip"ngx_str_t name;/*配置項(xiàng)類型,type將指定配置項(xiàng)可以出現(xiàn)的位置。例如,出現(xiàn)在server{}或location{}中,以及它可以攜帶的參數(shù)個(gè)數(shù) */ngx_uint_t type;//出現(xiàn)了name中指定的配置項(xiàng)后,將會(huì)調(diào)用set方法處理配置項(xiàng)的參數(shù)char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);//在配置文件中的偏移量ngx_uint_t conf;/*通常用于使用預(yù)設(shè)的解析方法解析配置項(xiàng),這是配置模塊的一個(gè)優(yōu)秀設(shè)計(jì)。*/ngx_uint_t offset;//配置項(xiàng)讀取后的處理方法,必須是ngx_conf_post_t結(jié)構(gòu)的指針void *post; };

? ?ngx_null_command只是一個(gè)空的ngx_command_t,如下所示:

?#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }

22.? ?

typedef enum {//在接收到完整的HTTP頭部后處理的HTTP階段NGX_HTTP_POST_READ_PHASE = 0,/*在還沒有查詢到URI匹配的location前,這時(shí)rewrite重寫URL也作為一個(gè)獨(dú)立的HTTP階段*/NGX_HTTP_SERVER_REWRITE_PHASE,/*根據(jù)URI尋找匹配的location,這個(gè)階段通常由ngx_http_core_module模塊實(shí)現(xiàn),不建議其他HTTP模塊重新定義這一階段的行為*/NGX_HTTP_FIND_CONFIG_PHASE,/*在NGX_HTTP_FIND_CONFIG_PHASE階段之后重寫URL的意義與NGX_HTTP_SERVER_REWRITE_PHASE階段顯然是不同的,因?yàn)檫@兩者會(huì)導(dǎo)致查找到不同的location塊(location是與URI進(jìn)行匹配的) */NGX_HTTP_REWRITE_PHASE,/* 這一階段是用于在rewrite重寫URL后重新跳到NGX_HTTP_FIND_CONFIG_PHASE階段,找到與心得URI匹配的location。所以,這一階段是無法由第三方HTTP模塊處理的,而僅由ngx_http_core_module模塊使用*/NGX_HTTP_POST_REWRITE_PHASE,//處理NGX_HTTP_ACCESS_PHASE階段前,HTTP模塊可以介入的處理階段NGX_HTTP_PREACCESS_PHASE,/*這個(gè)階段用于讓HTTP模塊判斷是否允許這個(gè)請(qǐng)求訪問Nginx服務(wù)器*/NGX_HTTP_ACCESS_PHASE,/*當(dāng)NGX_HTTP_ACCESS_PHASE階段中HTTP模塊的handler處理方法返回不允許訪問的錯(cuò)誤碼時(shí)(實(shí)際是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED),這個(gè)階段將負(fù)責(zé)構(gòu)造拒絕服務(wù)的用戶響應(yīng)。所以,這個(gè)階段實(shí)際上用于給NGX_HTTP_ACCESS_PHASE階段收尾*/NGX_HTTP_POST_ACCESS_PHASE,/*這個(gè)階段完全是為了try_files配置項(xiàng)而設(shè)計(jì)的。當(dāng)HTTP請(qǐng)求訪問靜態(tài)文件資源時(shí),try_files配置項(xiàng)可以使這個(gè)請(qǐng)求順序地訪問多個(gè)靜態(tài)文件資源,如果某一次訪問失敗,則繼續(xù)訪問try_files中指定的下一個(gè)靜態(tài)資源。另外,這個(gè)功能完全是在NGX_HTTP_TRY_FILES_PHASE階段中實(shí)現(xiàn)的 */NGX_HTTP_TRY_FILES_PHASE,//用于處理HTTP請(qǐng)求內(nèi)容的階段,這是大部分HTTP模塊最喜歡介入的階段NGX_HTTP_CONTENT_PHASE,/* 處理完請(qǐng)求后記錄日志的階段,例如,ngx_http_log_module模塊就在這個(gè)階段中加入了一個(gè)handler處理方法,使得每個(gè)HTTP請(qǐng)求處理完畢后會(huì)記錄access_log日志 */NGX_HTTP_LOG_PHASE } ngx_http_phases;

23.處理方法的返回值,其中包括了HTTP框架已經(jīng)在/src/http/ngx_http_request.h文件中定義好的宏,如下所示:

#define NGX_HTTP_OK 200 #define NGX_HTTP_CREATED 201 #define NGX_HTTP_ACCEPTED 202 #define NGX_HTTP_NO_CONTENT 204 #define NGX_HTTP_PARTIAL_CONTENT 206#define NGX_HTTP_SPECIAL_RESPONSE 300 #define NGX_HTTP_MOVED_PERMANENTLY 301 #define NGX_HTTP_MOVED_TEMPORARILY 302 #define NGX_HTTP_SEE_OTHER 303 #define NGX_HTTP_NOT_MODIFIED 304 #define NGX_HTTP_TEMPORARY_REDIRECT 307#define NGX_HTTP_BAD_REQUEST 400 #define NGX_HTTP_UNAUTHORIZED 401 #define NGX_HTTP_FORBIDDEN 403 #define NGX_HTTP_NOT_FOUND 404 #define NGX_HTTP_NOT_ALLOWED 405 #define NGX_HTTP_REQUEST_TIME_OUT 408 #define NGX_HTTP_CONFLICT 409 #define NGX_HTTP_LENGTH_REQUIRED 411 #define NGX_HTTP_PRECONDITION_FAILED 412 #define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE 414 #define NGX_HTTP_REQUEST_URI_TOO_LARGE 414 #define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415 #define NGX_HTTP_RANGE_NOT_SATISFIABLE 416/* The special code to close connection without any response */ #define NGX_HTTP_CLOSE 444 #define NGX_HTTP_NGINX_CODES 494 #define NGX_HTTP_REQUEST_HEADER_TOO_LARGE 494 #define NGX_HTTPS_CERT_ERROR 495 #define NGX_HTTPS_NO_CERT 496#define NGX_HTTP_TO_HTTPS 497 #define NGX_HTTP_CLIENT_CLOSED_REQUEST 499#define NGX_HTTP_INTERNAL_SERVER_ERROR 500 #define NGX_HTTP_NOT_IMPLEMENTED 501 #define NGX_HTTP_BAD_GATEWAY 502 #define NGX_HTTP_SERVICE_UNAVAILABLE 503 #define NGX_HTTP_GATEWAY_TIME_OUT 504 #define NGX_HTTP_INSUFFICIENT_STORAGE 507

24.請(qǐng)求的所有信息(如方法、URI、協(xié)議版本號(hào)和頭部等)都可以在傳入的ngx_http_request_t類型參數(shù)r中取得。

typedef struct ngx_http_request_s ngx_http_request_t; struct ngx_http_request_s {...ngx_uint_t method;ngx_uint_t http_version;ngx_str_t request_line;ngx_str_t uri;ngx_str_t args;ngx_str_t exten;ngx_str_t unparsed_uri; //表示沒有進(jìn)行URL解碼的原始請(qǐng)求。ngx_str_t method_name;ngx_str_t http_protocol;u_char *uri_start;u_char *uri_end;u_char *uri_ext;u_char *args_start;u_char *request_start;u_char *request_end;u_char *method_end;u_char *schema_start;u_char *schema_end;... };

25.ngx_http_headers_in_t類型的headers_in則存儲(chǔ)已經(jīng)解析過的HTTP頭部。

typedef struct {/* 所有解析過的HTTP頭部都在headers鏈表中,可以使用遍歷鏈表的方法來獲取所有的HTTP頭部。注意:這里headers鏈表的每一個(gè)元素都是ngx_table_elt_t成員*/ngx_list_t headers;/*以下每個(gè)ngx_table_elt_t成員都是RFC1616規(guī)范中定義的HTTP頭部,它們實(shí)際都指向headers鏈表中的響應(yīng)成員。注意,當(dāng)它們?yōu)镹ULL空指針時(shí),表示沒有解析到響應(yīng)的HTTP頭部*/ngx_table_elt_t *host;ngx_table_elt_t *connection;ngx_table_elt_t *if_modified_since;ngx_table_elt_t *if_unmodified_since;ngx_table_elt_t *user_agent;ngx_table_elt_t *referer;ngx_table_elt_t *content_length;ngx_table_elt_t *content_type;ngx_table_elt_t *range;ngx_table_elt_t *if_range;ngx_table_elt_t *transfer_encoding;ngx_table_elt_t *expect;#if (NGX_HTTP_GZIP)ngx_table_elt_t *accept_encoding;ngx_table_elt_t *via; #endifngx_table_elt_t *authorization;ngx_table_elt_t *keep_alive; #if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)ngx_table_elt_t *x_forwarded_for; #endif#if (NGX_HTTP_REALIP)ngx_table_elt_t *x_real_ip; #endif#if (NGX-HTTP_HEADERS)ngx_table_elt_t *accept;ngx_table_elt_t *accept_language; #endif#if (NGX_HTTP_DAV)ngx_table_elt_t *depth;ngx_table_elt_t *destination;ngx_table_elt_t *overwrite;ngx_table_elt_t *date; #endif/* user和passwd是只有ngx_http_auth_basic_module才會(huì)用到的成員,這里可以忽略*/ngx_str_t user;ngx_str_t passwd;/* cookies是以ngx_array_t數(shù)組存儲(chǔ)的。*/ngx_array_t cookies;//server名稱ngx_str_t server;//根據(jù)ngx_table_elt_t *content_length計(jì)算出的HTTP包體代銷off_t content_length_n;time_t keep_alive_n;/* HTTP連接類型,它的取值范圍是0、NGX_http_CONNECTION_CLOSE或者NGX_HTTP_CONNECTION_KEEP_ALIVE */unsigned connection_type:2;/*以下7個(gè)標(biāo)志位是HTTP框架根據(jù)瀏覽器傳來的"useragent"頭部,它們可用來判斷瀏覽器的類型,值為1時(shí)表示是相應(yīng)的瀏覽器發(fā)來的請(qǐng)求,值為0時(shí)則相反 */unsigned msie:1;unsigned msie6:1;unsigned opera:1;unsigned gecko:1;unsigned chrome:1;unsigned safari:1;unsigned konqueror:1; } ngx_http_headers_in_t;

26.HTTP包體的長度有可能非常大,如果視圖一次性調(diào)用并讀取完所有的包體,那么多半會(huì)阻塞Nginx進(jìn)程,HTTP框架提供了一種方法來異步地接受包體:

ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler);

? ngx_http_read_client_request_body是一個(gè)異步方法,調(diào)用它只是說明要求Nginx開始接收請(qǐng)求的包體,并不表示是否已經(jīng)接收完,當(dāng)接收完所有的包體內(nèi)容后,post_handler指向的回調(diào)方法會(huì)被調(diào)用。因此,即使在調(diào)用了ngx_http-read_client_request_body方法后它已經(jīng)返回,也無法確定這時(shí)是否已經(jīng)調(diào)用過post_handler指向的方法。換句話說,ngx_http_read_client_request_body返回時(shí)既有可能已經(jīng)接收完請(qǐng)求中所有的包體(假設(shè)包體的長度很小),也有可能還沒開始接受包體。

27.HTTP框架提供的發(fā)送HTTP頭部的方法如下所示:

ngx_int_t ngx_http_send_header(ngx_http_request_t *r);

28.headers_out的結(jié)構(gòu)類型ngx_http_headers_out_t:

typedef struct {//待發(fā)送的HTTP頭部鏈表,與headers_in中的headers成員類似ngx_list_t headers;/*響應(yīng)中的狀態(tài)值,如200表示成功。*/ngx_str_t status;//響應(yīng)的狀態(tài)行,如"HTTP/1.1 201 CREATED"ngx_str_t status_line;/*以下成員(包括ngx_table_elt_t)都是RFC1616規(guī)范中定義的HTTP頭部,設(shè)置后,ngx_http_header_filter_module過濾模塊可以把它們加到待發(fā)送的網(wǎng)絡(luò)包中*/ngx_table_elt_t *server;ngx_table_elt_t *date;ngx_table_elt_t *content_length;ngx_table_elt_t *content_encoding;ngx_table_elt_t *location;ngx_table_elt_t *refresh;ngx_table_elt_t *last_modified;ngx_table_elt_t *content_range;ngx_table_elt_t *accept_ranges;ngx_table_elt_t *www_authenticate;ngx_table_elt_t *expires;ngx_table_elt_t *etag;ngx_str_t *override_charset;/* 可以調(diào)用ngx_http_set_content_type(r)方法幫助我們?cè)O(shè)置Content-Type頭部,這個(gè)方法會(huì)根據(jù)URI中的文件擴(kuò)展名餅對(duì)應(yīng)著mime.type來設(shè)置Content-Type值*/size_t content_type_len; ngx_str_t content_type;ngx_str_t charset;u_char *content_type_lowcase;ngx_uint_t content_type_hash;ngx_array_t cache_control;/*在這里指定過content_length_n后,不用再次到ngx_table_elt_t *content_length中設(shè)置響應(yīng)長度*/off_t content_length_n;time_t date_time;time_t last_modified_time; } ngx_http_headers_out_t;

? ?ngx_http_send_header方法會(huì)首先調(diào)用所有的HTTP過濾模塊共同處理headers_out中定義的HTTP響應(yīng)頭部,全部處理完畢后才會(huì)序列化為TCP字符流發(fā)送到客戶端。

28.注意:在向用戶發(fā)送響應(yīng)包體時(shí),必須牢記Nginx是全異步的服務(wù)器,也就是說,不可以在進(jìn)程的棧里分配內(nèi)存并將其作為包體發(fā)送。當(dāng)ngx_http_output_filter方法返回時(shí),可能由于TCP連接上的緩沖區(qū)還不可寫,所以導(dǎo)致ngx_buf_t緩沖區(qū)指向的內(nèi)存還沒有發(fā)送,可這時(shí)方法返回已把控制權(quán)交給Nginx了,又會(huì)導(dǎo)致棧里的內(nèi)存被釋放,最后就會(huì)造成內(nèi)存越界錯(cuò)誤。因此,在發(fā)送響應(yīng)包體時(shí),盡量將ngx_buf_t中的pos指針指向從內(nèi)存池里分配的內(nèi)存。

29.經(jīng)典的"Hello World"示例

static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) {//必須是GET或者HEAD方法,否則返回405 Not Allowedif (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {return NGX_HTTP_NOT_ALLOWED;}//丟棄請(qǐng)求中的包體ngx_int_t rc = ngx_http_discard_request_body(r);if (rc != NGX_OK) {return rc;}/* 設(shè)置返回的Content-Type.注意,ngx_str_t有一個(gè)很方便的初始化宏ngx_string,它可以把ngx_str_t的data和len成員都設(shè)置好 */ngx_str_t type = ngx_string("text/plain");//返回的包體內(nèi)容ngx_str_t type = ngx_string("Hello World!");//設(shè)置返回狀態(tài)碼r->headers_out.status = NGX_HTTP_OK;//響應(yīng)包是有包體內(nèi)容的,需要設(shè)置Content-Lenght的長度r->headers_out.content_length_n = response.len;//設(shè)置Content-Typer->headers_out.content_type = type;//發(fā)送HTTP頭部rc = ngx_http_send_header(r);if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {return rc;}//構(gòu)造ngx_buf_t結(jié)構(gòu)體準(zhǔn)備發(fā)送包體ngx_buf_t *b;b = ngx_create_temp_buf(r->pool, response.len);if (b == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}//將Hello World復(fù)制到ngx_buf_t指向的內(nèi)存中ngx_memcpy(b->pos, response.data, response.len);//注意,一定要設(shè)置好last指針b->last = b->pos + response.len;//聲明這是最后一塊緩沖區(qū)b->last_buf = 1;//構(gòu)造發(fā)送時(shí)的ngx_chain_t結(jié)構(gòu)體ngx_chain_t out;//賦值ngx_buf_tout.buf = b;//設(shè)置next為NULLout.next = NULL;/*最后一步為發(fā)送包體,發(fā)送結(jié)束后HTTP框架會(huì)調(diào)用ngx_http_finalize_request方法結(jié)束請(qǐng)求 */return ngx_http_output_filter(r, &out); }

30.ngx_file_t的結(jié)構(gòu)如下:

typedef struct ngx_file_s ngx_file_t; struct ngx_file_s {//文件句柄描述符ngx_fd_t fd;//文件名稱ngx_str_t name;//文件大小等資源信息,實(shí)際就是Linux系統(tǒng)定義的stat結(jié)構(gòu)ngx_file_info_t info;/*該偏移量告訴Nginx現(xiàn)在處理到文件何處了,一般不用設(shè)置它,Nginx框架會(huì)根據(jù)當(dāng)前發(fā)送狀態(tài)設(shè)置它*/off_t offset;//當(dāng)前文件系統(tǒng)偏移量,一般不用設(shè)置它off_t sys_offset;//日志對(duì)象,相關(guān)的日志會(huì)輸出到log指定的日志文件中ngx_log_t *log;//目前未使用unsigned valid_info:1;//與配置文件中的directio配置項(xiàng)相對(duì)應(yīng),在發(fā)送大文件時(shí)可以設(shè)為1unsigned directio:1; };

? Nginx不只對(duì)stat數(shù)據(jù)結(jié)構(gòu)做了封裝,對(duì)于由操作系統(tǒng)中獲取文件信息的stat方法,Nginx也使用一個(gè)宏進(jìn)行了簡(jiǎn)單的封裝,如下:

#define ngx_file_info(file, sb) stat((const char *) file, sb)

? ?之后必須要設(shè)置Content-Length頭部:

r->headers_out.content_length_n = b->file->info.st_size;

? ?還需要設(shè)置ngx_buf_t緩沖區(qū)的file_pod和file_last:

b->file_pos = 0; b->file_last = b->file->info.st_size;

? ?這里告訴Nginx從文件的file_pos偏移量開始發(fā)送文件,一直到達(dá)file_last偏移量處截止。

31. HTTP框架定義了3個(gè)級(jí)別的配置main、srv、loc,分別表示直接出現(xiàn)在http{}、server{}、location{}塊內(nèi)的配置項(xiàng)。當(dāng)nginx.conf中出現(xiàn)http{}時(shí),HTTP框架會(huì)接管配置文件中http{}塊內(nèi)的配置項(xiàng)解析。當(dāng)遇到http{...}配置塊時(shí),HTTP框架會(huì)調(diào)用所有HTTP模塊可能實(shí)現(xiàn)的create_main_conf、create_srv_conf、create_loc_conf方法生成存儲(chǔ)main級(jí)別配置參數(shù)的結(jié)構(gòu)體;在遇到server{...}塊時(shí)會(huì)再次調(diào)用所有HTTP模塊的create_srv_conf、create_loc_conf回調(diào)方法生成存儲(chǔ)srv級(jí)別配置參數(shù)的結(jié)構(gòu)體;在遇到location{...}時(shí)則會(huì)再次調(diào)用create_loc_conf回調(diào)方法生成存儲(chǔ)loc級(jí)別配置參數(shù)的結(jié)構(gòu)體。因此,實(shí)現(xiàn)這3個(gè)回調(diào)方法的意義是不同的。

32.設(shè)定配置項(xiàng)的解析方式

? ? ngx_command_t結(jié)構(gòu)詳解

struct ngx_command_s {ngx_str_t name;ngx_uint_t type;char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);ngx_uint_t conf;ngx_uint_t offset;void *post; };

? ? (1) ngx_str_t name

? ? ? ? 其中,name是配置項(xiàng)名稱。

? ? (2) ngx_uint_t type

? ? ? ? 其中,type決定這個(gè)配置項(xiàng)可以在哪些塊(如http、server、location、if、upstream塊等)中出現(xiàn),以及可以攜帶的參數(shù)類型和個(gè)數(shù)等。

? ? ? ? ? ngx_command_t結(jié)構(gòu)體中type成員的取值及其意義

type類型type取值意義
處理配置項(xiàng)時(shí)獲取當(dāng)前配置塊的方式NGX_DIRECT_CONF一般由NGX_CORE_MODULE類型的核心模塊使用,僅與下面的NGX_MAIN_CONF同時(shí)設(shè)置,表示模塊需要解析不屬于任何{}內(nèi)的全局配置項(xiàng)。它實(shí)際上會(huì)指定set方法里的第3個(gè)參數(shù)conf的值,使之指向每個(gè)模塊解析全局配置項(xiàng)的配置結(jié)構(gòu)體(表下面解釋)
NGX_ANY_CONF目前未使用,設(shè)置與否均無意義
配置項(xiàng)可以在哪些{}配置塊中出現(xiàn)NGX_MAIN_CONF配置項(xiàng)可以出現(xiàn)在全局配置中,即不屬于任何{}配置塊
NGX_EVENT_CONF配置項(xiàng)可以出現(xiàn)在events{}塊內(nèi)
NGX_MAIL_MAIN_CONF配置項(xiàng)可以出現(xiàn)在mail{}塊或者imap{}塊內(nèi)
NGX_MAIL_SRV_CONF配置項(xiàng)可以出現(xiàn)在server{}塊內(nèi),然而該server{}塊必須屬于mail{}塊或者imap{}塊
NGX_HTTP_MAIN_CONF配置項(xiàng)可以出現(xiàn)在http{}塊內(nèi)
NGX_HTTP_SRV_CONF配置項(xiàng)可以出現(xiàn)在server{}塊內(nèi),然而該server塊必須屬于http{}內(nèi)
NGX_HTTP_LOC_CONF

配置項(xiàng)可以出現(xiàn)在location{}塊內(nèi),然而該location塊必須屬于http{}內(nèi)

NGX_HTTP_UPS_CONF配置項(xiàng)可以出現(xiàn)在upstream{}塊內(nèi),然而該upstream塊必須屬于http{}塊
NGX_HTTP_SIF_CONF配置項(xiàng)可以出現(xiàn)在server塊內(nèi)的if{}塊中。目前僅有rewrite模塊會(huì)使用,該if塊必須屬于http{}塊
NGX_HTTP_LIF_CONF配置項(xiàng)可以出現(xiàn)在location塊內(nèi)的if{}塊中。目前僅有rewrite模塊會(huì)使用,該if塊必須屬于http{}塊
NGX_HTTP_LMT_CONF配置項(xiàng)可以出現(xiàn)在limit_except{}塊內(nèi),然而該limit_except塊必須屬于http{}塊
限制配置項(xiàng)的參數(shù)個(gè)數(shù)NGX_CONF_NOARGS配置項(xiàng)不攜帶任何參數(shù)
NGX_CONF_TAKE1配置項(xiàng)必須攜帶1個(gè)參數(shù)
NGX_CONF_TAKE2配置項(xiàng)必須攜帶2個(gè)參數(shù)
NGX_CONF_TAKE3配置項(xiàng)必須攜帶3個(gè)參數(shù)
NGX_CONF_TAKE4配置項(xiàng)必須攜帶4個(gè)參數(shù)
NGX_CONF_TAKE5配置項(xiàng)必須攜帶5個(gè)參數(shù)
NGX_CONF_TAKE6配置項(xiàng)必須攜帶6個(gè)參數(shù)
NGX_CONF_TAKE7配置項(xiàng)必須攜帶7個(gè)參數(shù)
NGX_CONF_TAKE12配置項(xiàng)可以攜帶1個(gè)參數(shù)或2個(gè)參數(shù)
NGX_CONF_TAKE13配置項(xiàng)可以攜帶1個(gè)參數(shù)或3個(gè)參數(shù)
NGX_CONF_TAKE23配置項(xiàng)可以攜帶2個(gè)參數(shù)或3個(gè)參數(shù)
NGX_CONF_TAKE123配置項(xiàng)可以攜帶1~3個(gè)參數(shù)
NGX_CONF_TAKE1234配置項(xiàng)可以攜帶1~4個(gè)參數(shù)
限制配置項(xiàng)后的參數(shù)出現(xiàn)的形式NGX_CONF_ARGS_NUMBERS目前未使用,無意義
NGX_CONF_BLOCK配置項(xiàng)定義了一種新的{}塊。例如,http、server、location等配置,它們的ttype都必須定義為NGX_CONF_BLOCK
NGX_CONF_ANY不驗(yàn)證配置項(xiàng)攜帶的參數(shù)個(gè)數(shù)
NGX_CONF_FLAG配置項(xiàng)攜帶的參數(shù)只能是1個(gè),并且參數(shù)的值只能是on或者off
NGX_CONF_1MORE配置項(xiàng)攜帶的參數(shù)個(gè)數(shù)必須超過1個(gè)
NGX_CONF_2MORE配置項(xiàng)攜帶的參數(shù)個(gè)數(shù)必須超過2個(gè)
NGX_CONF_MULTI表示當(dāng)前配置項(xiàng)可以出現(xiàn)在任意塊中(包括不屬于任何塊的全局配置),它僅用于配合其他配置項(xiàng)使用。type中未加入NGX_CONF_MULTI時(shí),如果一個(gè)配置項(xiàng)出現(xiàn)在type成員未標(biāo)明的配置塊中,那么Nginx會(huì)認(rèn)為該配置項(xiàng)非法,最后將導(dǎo)致Nginx啟動(dòng)失敗。但如果type中加入了NGX_CONF_MULTI,則認(rèn)為該配置項(xiàng)一定是合法的,然而又會(huì)有兩種不同的結(jié)果: a.如果配置項(xiàng)出現(xiàn)在type指示的塊中,則會(huì)調(diào)用set方法解析配置項(xiàng);b.如果配置項(xiàng)沒有出現(xiàn)在type指示的塊中,則不對(duì)該配置項(xiàng)做任何處理。因此,NGX_CONF_MULTI會(huì)使得配置項(xiàng)出現(xiàn)在未知塊中時(shí)不會(huì)出錯(cuò)。目前,還沒有官方模塊使用過NGX_CONF_MULTI.

解釋:每個(gè)進(jìn)程總都有一個(gè)唯一的ngx_cycle_t核心結(jié)構(gòu)體,它有一個(gè)成員conf_ctx維護(hù)著所有模塊的配置結(jié)構(gòu)體,其類型是void ****。conf_ctx意義為首先指向一個(gè)成員皆為指針的數(shù)組,其中每個(gè)成員指針又指向另外一個(gè)成員皆為指針的數(shù)組,第2個(gè)子數(shù)組中的成員指針才會(huì)指向各模塊生成的配置結(jié)構(gòu)體。這正是為了事件模塊、http模塊、mail模塊而設(shè)計(jì)的,這有利于不同于NGX_CORE_MODULE類型的特定模塊解析配置項(xiàng)。然而,NGX_CORE_MODULE類型的核心模塊解析配置項(xiàng)時(shí),配置項(xiàng)一定是全局的,不會(huì)從屬于任何{}配置塊的,它不需要上述這種雙數(shù)組設(shè)計(jì)。解析標(biāo)識(shí)為NGX_DIRECT_CONF類型的配置項(xiàng)時(shí),會(huì)把void ****類型的conf_ctx強(qiáng)制轉(zhuǎn)換為void **,也就是說,此時(shí),在conf_ctx指向的指針數(shù)組中,每個(gè)成員指針不再指向其他數(shù)組,直接指向核心模塊生成的配置結(jié)構(gòu)體。因此,NGX_DIRECT_CONF僅由NGX_CORE_MODULE類型的核心模塊使用,而且配置項(xiàng)只應(yīng)該出現(xiàn)在全局配置中。

? ?c. char*(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

? ? 預(yù)設(shè)的14個(gè)配置項(xiàng)解析方法

預(yù)設(shè)方法名行為
ngx_conf_set_flag_slot如果ngxin.conf文件中某個(gè)配置項(xiàng)的參數(shù)是on或者off(即希望配置項(xiàng)表達(dá)打開或者關(guān)閉某個(gè)功能的意思),而且在Nginx模塊的代碼中使用ngx_flag_t變量來保存這個(gè)配置項(xiàng)的參數(shù),就可以將set回調(diào)方法設(shè)為ngx_conf_set_flag_slot。當(dāng)nginx.conf文件中參數(shù)是on時(shí),代碼中的ngx_flag_t類型變量將設(shè)為1,參數(shù)為off時(shí)則設(shè)為0
ngx_conf_set_str_slot如果配置項(xiàng)后只有1個(gè)參數(shù),同時(shí)在代碼中我們希望用ngx_str_t類型的變量來保存這個(gè)配置項(xiàng)的參數(shù),則可以使用ngx_conf_set_str_slot方法
ngx_conf_set_str_array_slot如果這個(gè)配置項(xiàng)會(huì)出現(xiàn)多次,每個(gè)配置項(xiàng)后面都跟著1個(gè)參數(shù),而在程序中我們希望僅用一個(gè)ngx_array_t動(dòng)態(tài)數(shù)組來存儲(chǔ)所有的參數(shù),且數(shù)組中的每個(gè)參數(shù)都以ngx_str_t來存儲(chǔ),那么預(yù)設(shè)的ngx_conf_set_str_array_slot方法可以幫我們做到
ngx_conf_set_keyval_slot與ngx_conf_set_str_array_slot類似,也使用用一個(gè)ngx_array_t數(shù)組來存儲(chǔ)所有同名配置項(xiàng)的參數(shù)。只是每個(gè)配置項(xiàng)的參數(shù)不再只有1個(gè),而必須是兩個(gè),且以“配置項(xiàng)名 關(guān)鍵字 值;”的形式出現(xiàn)在nginx.conf文件中,同時(shí),ngx_conf_set_keyval_slot將把這些配置項(xiàng)轉(zhuǎn)化為數(shù)組,其中每個(gè)元素都存儲(chǔ)著key/value鍵值對(duì)。
ngx_conf_set_num_slot

配置項(xiàng)后必須攜帶1個(gè)參數(shù),且只能是數(shù)字。存儲(chǔ)這個(gè)參數(shù)的變量必須是整形

ngx_conf_set_size_slot

配置項(xiàng)后必須攜帶1個(gè)參數(shù),表示空間大小,可以是一個(gè)數(shù)字,這時(shí)表示字節(jié)數(shù)(Byte)。如果數(shù)字后跟著k或者K,就表示Kilobyt,1KB=1024B;如果數(shù)字后跟著m或者M(jìn),就表示Megabyte,1MB=1024KB。ngx_conf_set_size_slot解析后將把配置項(xiàng)后的參數(shù)轉(zhuǎn)化成以字節(jié)數(shù)為單位的數(shù)字

ngx_conf_set_off_slot配置項(xiàng)后必須攜帶1個(gè)參數(shù),表示空間上的偏移量。它與設(shè)置的參數(shù)非常類似,其參數(shù)是一個(gè)數(shù)字時(shí)表示Byte,也可以在后面加單位,但與ngx_conf_set_size_slot不同的是,數(shù)字后面的單位不僅可以是k或者K、m或者M(jìn),還可以是g或者G,這時(shí)表示Gigabyte,1GB=1024MB,ngx_conf_set_off_slot解析后將把配置項(xiàng)后的參數(shù)轉(zhuǎn)化成以字節(jié)數(shù)為單位的數(shù)字
ngx_conf_set_msec_slot配置項(xiàng)后必須攜帶1個(gè)參數(shù),表示時(shí)間。這個(gè)參數(shù)可以在數(shù)字后面加單位,如果單位為s或者沒有任何單位,那么這個(gè)數(shù)字表示秒;如果單位為m,則表示分鐘,1m=60s;如果單位為h,則表示小時(shí),1h=60m;如果單位為d,則表示天,1d=24h;如果單位為w,則表示周,1w=7d;如果單位為M,則表示月,1M=30d;如果單位為y,則表示年,1y=265d。ngx_conf_set_msec_slot解析后將把配置項(xiàng)后的參數(shù)轉(zhuǎn)化成以毫秒為單位的數(shù)字
ngx_conf_set_sec_slot與ngx_conf_set_msec_slot非常類似,唯一的區(qū)別是ngx_conf_set_msec_slot解析后將把配置項(xiàng)后的參數(shù)轉(zhuǎn)化成以毫秒為單位的數(shù)字,而ngx_conf_set_sec_slot解析后會(huì)把配置項(xiàng)后的參數(shù)轉(zhuǎn)化成以秒為單位的數(shù)字
ngx_conf_set_bufs_slot配置項(xiàng)后必須攜帶一兩個(gè)參數(shù),第1個(gè)參數(shù)是數(shù)字,第2個(gè)參數(shù)表示空間大小。例如:"gzip_buffers 4 8k;"(通常用來表示有多少個(gè)ngx_buf_t緩沖區(qū)),其中第1個(gè)參數(shù)不可以攜帶任何單位,第2個(gè)參數(shù)不帶任何單位時(shí)表示Byte,如果yik或者K作為單位,則表示Kilobyte,如果以m或者M(jìn)作為單位,則表示Megabyte。ngx_conf_set_bufs_slot解析后會(huì)把配置項(xiàng)后的兩個(gè)參數(shù)轉(zhuǎn)化成ngx_bufs_t結(jié)構(gòu)體下的兩個(gè)成員,這個(gè)配置項(xiàng)對(duì)應(yīng)于Nginx最喜歡用的多緩沖區(qū)的解決方案(如接受連接對(duì)端發(fā)來的TCP流)
ngx_conf_set_enum_slot配置項(xiàng)后必須攜帶1個(gè)參數(shù),其取值范圍必須是我們?cè)O(shè)定好的字符串之一(就像C語言中的枚舉一樣),首先,我們要用ngx_conf_enum_t結(jié)構(gòu)定義配置項(xiàng)的取值范圍,并設(shè)定每個(gè)值對(duì)應(yīng)的序列號(hào),然后,ngx_conf_set_enum_slot將會(huì)把配置項(xiàng)參數(shù)轉(zhuǎn)化為對(duì)應(yīng)的序列號(hào)。
ngx_conf_set_bitmask_slot與ngx_conf_set_bitmask_slot類似,配置項(xiàng)后必須攜帶1個(gè)參數(shù),其取值范圍必須是設(shè)定好的字符串只注意。首先,我們要用ngx_conf_bitmask_t結(jié)構(gòu)定義配置項(xiàng)的取值范圍,并設(shè)定每個(gè)值對(duì)應(yīng)的比特位。注意,每個(gè)值所對(duì)應(yīng)的比特位都要不同,然后ngx_conf_set_bitmask_slot將會(huì)把配置項(xiàng)參數(shù)轉(zhuǎn)化為對(duì)應(yīng)的比特位
ngx_conf_set_access_slot這個(gè)方法用于設(shè)置目錄或者文件的讀寫權(quán)限。配置項(xiàng)后可以攜帶1~3個(gè)參數(shù),可以是如下形式:user:rw group:rw all:rw。注意,它的意義與Linux上文件或者目錄的權(quán)限意義視一致的,但是user/group/all后面的權(quán)限只可以設(shè)為rw(讀/寫)或者r(只讀),不可以有其他任何形式,如w或者rx等。ngx_conf_set_access_slot將會(huì)把這些參數(shù)轉(zhuǎn)化為一個(gè)整形。
ngx_conf_set_path_slot這個(gè)方法用于設(shè)置路徑,配置項(xiàng)后必須攜帶1個(gè)參數(shù),表示1個(gè)有意義的路徑。ngx_conf_set_path_slot將會(huì)把參數(shù)轉(zhuǎn)化為ngx_path_t結(jié)構(gòu)。

? ? d.ngx_uint_t conf

? ? ? ?conf用于指示配置項(xiàng)所處內(nèi)存的相對(duì)偏移位置,僅在type中沒有設(shè)置NGX_DIRECT_CONF和NGX_MAIN_CONF時(shí)才會(huì)生效。對(duì)于HTTP模塊,conf是必須要設(shè)置的,它的取值范圍如下:

conf在HTTP模塊中的取值意義
NGX_HTTP_MAIN_CONF_OFFSET使用create_main_conf方法產(chǎn)生的結(jié)構(gòu)體來存儲(chǔ)解析出的配置項(xiàng)參數(shù)
NGX_HTTP_SRV_CONF_OFFSET使用create_srv_conf方法產(chǎn)生的結(jié)構(gòu)體來存儲(chǔ)解析出的配置項(xiàng)參數(shù)
NGX_HTTP_LOC_CONF_OFFSET使用create_loc_conf方法產(chǎn)生的結(jié)構(gòu)體來存儲(chǔ)解析出的配置項(xiàng)參數(shù)

? ? e.ngx_uint_t offset

? ? ? offset表示當(dāng)前配置項(xiàng)在整個(gè)存儲(chǔ)配置項(xiàng)啊的結(jié)構(gòu)體中的偏移位置(以字節(jié)(Byte)為單位)。

? ? f.void *post

? ? ?一般置為NULL。

33.解析HTTP配置的流程

? ??

? ?(1) 主循環(huán)是指Nginx進(jìn)程的主循環(huán),主循環(huán)只有調(diào)用配置文件解析器才能解析nginx.conf文件(這里的"主循環(huán)"是指解析全部配置文件的循環(huán)代碼)。

? ?(2) 當(dāng)發(fā)現(xiàn)配置文件中含有http{}關(guān)鍵字時(shí),HTTP框架開始啟動(dòng),這一過程見ngx_http_block方法。

? ?(3) HTTP框架會(huì)初始化所有HTTP模塊的序列號(hào),并創(chuàng)建3個(gè)數(shù)組用于存儲(chǔ)所有HTTP模塊的create_main_conf、create_srv_conf、create_loc_conf方法返回的指針地址,并把這3個(gè)數(shù)組的地址保存到ngx_http_conf_ctx_t結(jié)構(gòu)中。

? ?(4) 調(diào)用每個(gè)HTTP模塊(當(dāng)然也包括例子中的mytest模塊)的create_main_conf、create_srv_conf、create_loc_conf(如果實(shí)現(xiàn)的話)方法。

? ?(5) 把各HTTP模塊上述3個(gè)方法的地址依次保存到ngx_http_conf_ctx_t結(jié)構(gòu)體的3個(gè)數(shù)組中。

? ?(6) 調(diào)用每個(gè)HTTP模塊的preconfiguration方法(如果實(shí)現(xiàn)的話).

? ?(7) 注意,如果preconfiguration返回失敗,那么Nginx進(jìn)程將會(huì)停止。

? ?(8) HTTP框架開始循環(huán)解析nginx.conf文件中http{...}里面的所有配置項(xiàng),注意,這個(gè)過程到第19步才會(huì)返回。

? ?(9) 配置文件解析器在檢測(cè)到1個(gè)配置項(xiàng)后,會(huì)遍歷所有的HTTP模塊,檢查它們的ngx_command_t數(shù)組中的name項(xiàng)是否與配置項(xiàng)名相同。

? ? (10) 如果找到有1個(gè)HTTP模塊對(duì)這個(gè)配置項(xiàng)感興趣,就調(diào)用ngx_command_t結(jié)構(gòu)中的set方法來處理。

? ? (11) set方法返回是否處理成功。如果處理失敗,那么Nginx進(jìn)程會(huì)停止。

? ? (12) 配置文件解析器繼續(xù)監(jiān)測(cè)配置項(xiàng)。如果發(fā)現(xiàn)server{...}配置項(xiàng),就會(huì)調(diào)用ngx_http_core_module模塊來處理。因?yàn)閚gx_http_core_module模塊明確表示希望處理server{}塊下的配置項(xiàng)。注意,這次調(diào)用到第18步才會(huì)返回。

? ? (13) ngx_http_core_module模塊在解析server{...}之前,也會(huì)如第3步一樣建立ngx_http_conf_ctx_t結(jié)構(gòu),并建立數(shù)組保存所有HTTP模塊返回的指針地址。然后,它會(huì)調(diào)用每個(gè)HTTP模塊的create_srv_conf、create_loc_conf方法(如果實(shí)現(xiàn)的話).

? ? (14) 將上一步各HTTP模塊返回的指針地址保存到ngx_http_conf_ctx_t對(duì)應(yīng)的數(shù)組中。

? ? (15) 開始調(diào)用配置文件解析器來處理server{...}里面的配置項(xiàng),注意,這個(gè)過程在第17步返回。

? ? (16) 繼續(xù)重復(fù)第9步的過程,遍歷nginx.conf中當(dāng)前server{...}內(nèi)的所有配置項(xiàng)。

? ? (17)配置文件解析器繼續(xù)解析配置項(xiàng),發(fā)現(xiàn)當(dāng)前server塊已經(jīng)遍歷到尾部,說明server塊內(nèi)的配置項(xiàng)處理完畢,返回ngx_http_core_module模塊。

? ? (18) http core模塊也處理完server配置項(xiàng)了,返回至配置文件解析器繼續(xù)解析后面的配置項(xiàng)。

? ? (19) 配置文件解析器繼續(xù)解析配置項(xiàng),這時(shí)發(fā)現(xiàn)處理到了http{...}的尾部,返回給HTTP框架繼續(xù)處理。

? ? (20) 在第3步和第13步,以及我們沒有列出來的某些步驟中(如發(fā)現(xiàn)其他server塊或者location塊),都創(chuàng)建了ngx_http_conf_ctx_t結(jié)構(gòu)體,這時(shí)將開始調(diào)用merge_srv_conf、merge_loc_conf等方法合并這些不同塊(http、server、location)中每個(gè)HTTP模塊分配的數(shù)據(jù)結(jié)構(gòu)。

? ? (21) HTTP框架處理完畢http配置項(xiàng)(也就是ngx_command_t結(jié)構(gòu)中的set回調(diào)方法處理完畢),返回給配置文件解析器繼續(xù)處理其他http{...}外的配置項(xiàng)。

? ? (22) 配置文件解析器處理完所有配置項(xiàng)后會(huì)告訴Nginx主循環(huán)配置項(xiàng)解析完畢,這時(shí)Nginx才會(huì)啟動(dòng)Web服務(wù)器。

34.http塊與server塊下的ngx_http_conf_ctx_t所指向的內(nèi)存間的關(guān)系

? ?

35.合并配置項(xiàng)過程的活動(dòng)圖,主要包含四大部分內(nèi)容:

? ?(1) 如果HTTP模塊實(shí)現(xiàn)了merge_srv_conf方法,就將http{...}塊下create_srv_conf生成的結(jié)構(gòu)體與遍歷每一個(gè)server{...}配置塊下的結(jié)構(gòu)體做merge_srv_conf操作;

? ?(2) 如果HTTP模塊實(shí)現(xiàn)了merge_loc_conf方法,就將http{...}塊下create_loc_conf生成的結(jié)構(gòu)體與嵌套的每一個(gè)server{...}配置塊下生成的結(jié)構(gòu)體做merge_loc_conf操作;

? ?(3) 如果HTTP模塊實(shí)現(xiàn)了merge_loc_conf方法,就將server{...}塊下create_loc_conf生成的結(jié)構(gòu)體與嵌套的每一個(gè)location{...}配置塊下create_loc_conf生成的數(shù)據(jù)結(jié)構(gòu)做merge_loc_conf操作;

? ?(4) 如果HTTP模塊實(shí)現(xiàn)了merge_loc_conf方法,就將location{...}塊下create_loc_conf生成的結(jié)構(gòu)體與繼續(xù)嵌套的每一個(gè)location{...}配置塊下create_loc_conf生成的數(shù)據(jù)結(jié)構(gòu)做merge_loc_conf操作。

? ?

? 上圖中包括4重循環(huán),第1層(最外層)遍歷所有的HTTP模塊,第2層遍歷所有的server{...}配置塊,第3層是遍歷某個(gè)server{}塊中嵌套的所有l(wèi)ocation{...}塊,第4層遍歷某個(gè)location{}塊中繼續(xù)嵌套的所有l(wèi)ocation塊(實(shí)際上,它會(huì)一直遞歸下去以解析可能被層層嵌套的location塊).

36.請(qǐng)求的上下文

? ? 在Nginx中,上下文有很多種含義。HTTP框架定義的這個(gè)上下文是針對(duì)于HTTP請(qǐng)求的,而且一個(gè)HTTP請(qǐng)求對(duì)應(yīng)于每一個(gè)HTTP模塊都可以有一個(gè)獨(dú)立的上下文結(jié)構(gòu)體(并不是一個(gè)請(qǐng)求的上下文由所有HTTP模塊共用)。

37.ngx_http_get_module_ctx和ngx_http_set_ctx這兩個(gè)宏可以完成HTTP上下文的設(shè)置和使用。

#define ngx_http_get_module_ctx(r, module) (r)->ctx[module.ctx_index] #define ngx_http_set_ctx(r, c, module) r->ctx[module.ctx_index] = c;

? ?當(dāng)請(qǐng)求第1次進(jìn)入mytest模塊處理時(shí),創(chuàng)建ngx_http_mytest_ctx_t結(jié)構(gòu)體,并設(shè)置到這個(gè)請(qǐng)求的上下文中。

static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) {//首先調(diào)用ngx_http_get_module_ctx宏來獲取上下文結(jié)構(gòu)體ngx_http_mytest_ctx_t *myctx = ngx_http_get_module_ctx(r, ngx_http_mytest_module);//如果之前沒有設(shè)置過上下文,那么應(yīng)當(dāng)返回NULL。if (myctx == NULL) {/*必須在當(dāng)前請(qǐng)求的內(nèi)存池r->pool中分配上下文結(jié)構(gòu)體,這樣請(qǐng)求結(jié)束時(shí)結(jié)構(gòu)體占用的內(nèi)存才會(huì)釋放*/myctx = ngx_palloc(r->pool, sizeof(ngx_http_mytest_ctx_t));if (myctx == NULL) {return NGX_ERROR;}//將剛分配的結(jié)構(gòu)體設(shè)置到當(dāng)前請(qǐng)求的上下文中ngx_http_set_ctx(r, myctx, ngx_http_mytest_module);}//之后可以任意使用myctx這個(gè)上下文結(jié)構(gòu)體... }

38.模塊在處理任何一個(gè)請(qǐng)求時(shí)都有ngx_http_request_t結(jié)構(gòu)的對(duì)象r,而請(qǐng)求r中又有一個(gè)ngx_http_upstream_t類型的成員upstream.

typedef struct ngx_http_request_s ngx_http_request_t; struct ngx_http_request_s {...ngx_http_upstream_t *upstream;... };

39.啟動(dòng)upstream的流程圖

? ?

40.upstream執(zhí)行的一般流程

??

41.ngx_http_upstream_t結(jié)構(gòu)體

typedef struct ngx_http_upstream_s ngx_http_upstream_t; struct ngx_http_upstream_s {.../* request_bufs決定發(fā)送什么樣的請(qǐng)求給上游服務(wù)器,在實(shí)現(xiàn)create_request方法是需要設(shè)置它 */ngx_chain_t *request_bufs;//upstream訪問時(shí)的所有限制性參數(shù)ngx_http_upstream_conf_t *conf;//通過resolved可以直接指定上游服務(wù)器地址ngx_http_upstream_resolved_t *resolved;/* buffer成員存儲(chǔ)接收自上游服務(wù)器發(fā)來的響應(yīng)內(nèi)容,由于它會(huì)被復(fù)用,所以具有下例多種意義:* a) 在使用process_header方法解析上游響應(yīng)的包頭時(shí),buffer中將會(huì)保存完整的響應(yīng)包頭;* b) 當(dāng)下面的buffering成員為1,而且此時(shí)upstream是向下游轉(zhuǎn)發(fā)上游的包體時(shí),buffer沒有意義;* c) 當(dāng)buffering標(biāo)志位位0時(shí),buffer緩沖區(qū)會(huì)被用于反復(fù)地接收上游的包體,進(jìn)而向下游轉(zhuǎn)發(fā);* d) 當(dāng)upstream并不用于轉(zhuǎn)發(fā)上游包體時(shí),buffer會(huì)被用于反復(fù)接收上游的包體,HTTP模塊實(shí)現(xiàn)的input_filter方法需要關(guān)注它*/ngx_buf_t buffer;//構(gòu)造發(fā)往上游服務(wù)器的請(qǐng)求內(nèi)容ngx_int_t (*create_request)(ngx_http_request_t *r);/* 銷毀upstream請(qǐng)求時(shí)調(diào)用 */void (*finalize_request)(ngx_http_request_t *r, ngx_int_t rc);//5個(gè)可選的回調(diào)方法ngx_int_t (*input_filter_init)(void *data);ngx_int_t (*input_filter)(void *data, ssize_t bytes);ngx_int_t (*reinit_request)(ngx_http_request_t *r);void (*abort_request)(ngx_http_request_t *r);ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix);//是否基于SSL協(xié)議訪問上游服務(wù)器unsigned ssl:1;/* 在向客戶端轉(zhuǎn)發(fā)上游服務(wù)器的包體時(shí)才有用。當(dāng)buffering為1時(shí),表示使用多個(gè)緩沖區(qū)以及磁盤文件來轉(zhuǎn)發(fā)上有的響應(yīng)包體。當(dāng)Nginx與上游間的網(wǎng)速遠(yuǎn)大于Nginx與下游客戶端見的網(wǎng)速時(shí),讓Nginx開辟更多的內(nèi)存甚至使用磁盤文件來緩存上游的響應(yīng)包體,這是有意義的,它可以減輕上游服務(wù)器的并發(fā)壓力。當(dāng)buffering為0時(shí),表示只使用上面的這一個(gè)buffer緩沖區(qū)來向下游轉(zhuǎn)發(fā)響應(yīng)包體*/unsigned buffering:1;... };

42.設(shè)置upstream的限制性參數(shù)

? ? ngx_http_upstream_t中的conf成員,它用于設(shè)置upstream模塊處理請(qǐng)求時(shí)的參數(shù),包括連接、發(fā)送、接收的超時(shí)時(shí)間等。

typedef struct {...//連接上游服務(wù)器的超時(shí)時(shí)間,單位為毫秒ngx_msec_t connect_timeout;//發(fā)送TCP包到上游服務(wù)器的超時(shí)時(shí)間,單位為毫秒ngx_msec_t send_timeout;//接收TCP包到上游服務(wù)器的超時(shí)時(shí)間,單位為毫秒ngx_msec_t read_timeout;... } ngx_http_upstream_conf_t;

43.ngx_http_upstream_t結(jié)構(gòu)中的resolved成員可以直接設(shè)置上游服務(wù)器的地址,首先介紹一下resolved的類型:

typedef struct {...//地址個(gè)數(shù)ngx_uint_t naddrs;//上游服務(wù)器的地址struct sockaddr *sockaddr;socklen_t socklen;... } ngx_http_upstream_resolved_t;

44.直接執(zhí)行ngx_http_upstream_init方法即可啟動(dòng)upstream機(jī)制。

45.create_request回調(diào)方法

? ??

? ? 如上圖,步驟分別如下:

? ? 1) 在Nginx主循環(huán)(之類的主循環(huán)是指ngx_worker_process_cycle方法)中,會(huì)定期地調(diào)用事件模塊,以檢查是否有網(wǎng)絡(luò)事件發(fā)生。

? ? 2)事件模塊在接收到HTTP請(qǐng)求后會(huì)調(diào)用HTTP框架來處理。假設(shè)接收、解析完HTTP頭部后發(fā)現(xiàn)應(yīng)該由mytest模塊處理,這時(shí)會(huì)調(diào)用mytest模塊的ngx_http_mytest_handler來處理。

? ? 3)設(shè)置回調(diào)函數(shù)和第三方地址;

? ? 4)調(diào)用ngx_http_upstream_init方法啟動(dòng)upstream;

? ? 5)upstream模塊會(huì)去檢查文件緩存,如果緩存中已經(jīng)有合適的響應(yīng)包,則會(huì)直接返回緩存(當(dāng)然必須是在使用反向代理文件緩存的前提下)。

? ? 6)回調(diào)mytest模塊已經(jīng)實(shí)現(xiàn)的create_request回調(diào)方法;

? ? 7)mytest模塊通過設(shè)置r->upstream->request_bufs已經(jīng)決定好發(fā)送什么樣的請(qǐng)求到上有服務(wù)器。

? ? 8)upstream模塊會(huì)檢查已經(jīng)介紹過的resolved成員,如果有resolved成員的話,就根據(jù)它設(shè)置好上游服務(wù)器的地址r->upstream->peer成員。

? ? 9)用無阻塞的TCP套接字建立連接;

? ? 10)無論連接是否建立成功,負(fù)責(zé)建立連接的connect方法都會(huì)立刻返回。

? ? 11)ngx_http_upstream_init返回;

? ? 12)mytest模塊的ngx_http_mytest_handler方法返回NGX_DONE。

? ? 13)當(dāng)事件模塊處理完這批網(wǎng)絡(luò)事件后,將控制權(quán)交還給Nginx主循環(huán).

46.reinit_request可能會(huì)被多次回調(diào)。它被調(diào)用的原因只有一個(gè),就是在第一次試圖向上游服務(wù)器建立連接時(shí),如果連接由于各種異常原因失敗,那么會(huì)根據(jù)upstream中conf參數(shù)的策略要求再次重連上游服務(wù)器,而這時(shí)就會(huì)調(diào)用reinit_request方法了。

? ??

? ? ? 上圖中的流程的步驟描述如下:

? ? ? 1) Nginx主循環(huán)中會(huì)定期地調(diào)用事件模塊,檢查是否有網(wǎng)絡(luò)事件發(fā)生。

? ? ? 2) 事件模塊在確定與上游服務(wù)器的TCP連接建立成功后,會(huì)回調(diào)upstream模塊的相關(guān)方法處理。

? ? ? 3) upstream模塊這時(shí)會(huì)把r->upstream->request_sent標(biāo)志位置為1,表示連接已經(jīng)建立成功了,現(xiàn)在開始向上游服務(wù)器發(fā)送請(qǐng)求內(nèi)容。

? ? ? 4) 發(fā)送請(qǐng)求到上游服務(wù)器。

? ? ? 5) 發(fā)送方法當(dāng)然是無阻塞的(使用了無阻塞的套接字),會(huì)立刻返回。

? ? ? 6) upstream模塊處理第2步中的TCP連接建立成功事件。

? ? ? 7) 事件模塊處理完本輪網(wǎng)絡(luò)事件后,將控制權(quán)交還給Nginx主循環(huán)。

? ? ? 8) Nginx主循環(huán)重復(fù)第1步,調(diào)用事件模塊檢查網(wǎng)絡(luò)事件。

? ? ? 9) 這時(shí),如果發(fā)現(xiàn)與上游服務(wù)器建立的TCP連接已經(jīng)異常斷開,那么事件模塊會(huì)通知upstream模塊處理它。

? ? ? 10) 在符合重試次數(shù)的前提下,upstream模塊會(huì)毫不猶豫地再次用無阻塞的套接字試圖建立連接。

? ? ? 11) 無論連接是否建立成功都立刻返回。

? ? ? 12) 這時(shí)檢查r->upstream->request_sent標(biāo)志位,會(huì)發(fā)現(xiàn)它已經(jīng)被置為1了。

? ? ? 13) 如果mytest模塊沒有實(shí)現(xiàn)reinit_request方法,那么是不會(huì)調(diào)用它的。而如果reinit_request不為NULL空指針,就會(huì)回調(diào)它。

? ? ? ?14) mytest模塊在reinit_request中處理完自己的事情。

? ? ? ?15) 處理完第9步中的TCP連接斷開事件,將控制權(quán)交還給事件模塊。

? ? ? ?16) 事件模塊處理完本輪網(wǎng)絡(luò)事件后,交還控制權(quán)給Nginx主循環(huán)。

47.finalize_request回調(diào)方法

? ? 當(dāng)調(diào)用ngx_http_upstream_init啟動(dòng)upstream機(jī)制后,在各種原因(無論成功還是失敗)導(dǎo)致該請(qǐng)求被銷毀前都會(huì)調(diào)用finalize_request方法。

? ? 在finalize_request方法中可以不做任何事情,但必須實(shí)現(xiàn)finalize_request方法,否則Nginx會(huì)出現(xiàn)空指針調(diào)用的嚴(yán)重錯(cuò)誤。

48.process_header回調(diào)方法

? ? process_header是用于解析上游服務(wù)器返回的基于TCP的響應(yīng)頭部的,因此,process_header可能會(huì)被多次調(diào)用,它的調(diào)用次數(shù)與process_header的返回值有關(guān)。如果process_header返回NGX_AGAIN,這意味著還沒有接收到完整的響應(yīng)頭部,如果再次接收到上游服務(wù)器發(fā)來的TCP流,還會(huì)把它當(dāng)做頭部,仍然調(diào)用process_header處理。如果process_header返回NGX_OK(或者其他非NGX_AGAIN的值),那么在這次連接的后續(xù)處理中將不會(huì)再次調(diào)用process_header。

? ?

? ? 上圖中的步驟解釋:
? ?1) Nginx主循環(huán)中會(huì)定期地調(diào)用事件模塊,檢查是否有網(wǎng)絡(luò)事件發(fā)生。

? ?2) 事件模塊接收到上游服務(wù)器發(fā)來的響應(yīng)時(shí),會(huì)回調(diào)upstream模塊處理。

? ?3) upstream模塊這時(shí)可以從套接字緩沖區(qū)中讀取到來自上游的TCP流。

? ?4) 讀取的響應(yīng)會(huì)存放到r->upstream->buffer指向的內(nèi)存中。注意:在未解析完響應(yīng)頭部前,若多次接收到字符流,所有接收自上游的響應(yīng)頭回完整地存放到r->upstream->buffer緩沖區(qū)中。因此,在解析上游響應(yīng)包頭時(shí),如果buffer緩沖區(qū)全滿卻還沒有解析到完整的響應(yīng)頭部(也就是說,process_header一直在返回NGX_AGAIN),那么請(qǐng)求就會(huì)出錯(cuò)。

? ?5) 調(diào)用mytest模塊實(shí)現(xiàn)的process_header方法。

? ?6) process_header方法實(shí)際上就是在解析r->upstream->buffer緩沖區(qū),試圖從中取到完整的響應(yīng)頭部(當(dāng)然,如果上游服務(wù)器與Nginx通過HTTP通信,就是接收到完整的HTTP頭部)。

? ?7) 如果process_header返回NGX_AGAIN,那么表示還沒有解析到完整的響應(yīng)頭部,下次還會(huì)調(diào)用process_header處理接收到的上游響應(yīng)。

? ?8) 調(diào)用無阻塞的讀取套接字接口。

? ?9) 這時(shí)有可能返回套接字緩沖區(qū)已經(jīng)為空。

? ?10) 當(dāng)?shù)?步中的讀取上游響應(yīng)時(shí)間處理完畢后,控制權(quán)交還給事件模塊。

? ?11) 事件模塊處理完本輪網(wǎng)絡(luò)事件后,交還控制權(quán)給Nginx主循環(huán)。

49.舉例說明upstream機(jī)制。實(shí)現(xiàn)的功能很簡(jiǎn)單:"即以訪問mytest模塊的URL參數(shù)作為搜索引擎的關(guān)鍵字,用upstream方式訪問google,查詢URL里的參數(shù),然后把google的結(jié)果返回給用戶。"

? ?每一個(gè)HTTP請(qǐng)求都會(huì)有獨(dú)立的ngx_http_upstream_conf_t結(jié)構(gòu)體,在mytest模塊的例子中,所有的請(qǐng)求都將共享同一個(gè)ngx_http_upstream_conf_t結(jié)構(gòu)體,因此,這里把它放到ngx_http_mytest_conf_t配置結(jié)構(gòu)體中,如下所示:

typedef struct {ngx_http_upstream_conf_t upstream; } ngx_http_mytest_conf_t;

? ?(1) 在啟動(dòng)upstream前,先將ngx_http_mytest_conf_t下的upstream成員賦給r->upstream->conf成員。

static void *ngx_http_mytest_create_loc_conf(ngx_conf_t *cf) {ngx_http_mytest_conf_t *mycf;mycf = (ngx_http_mytest_conf_t *)ngx_pcalloc(cf->pool, sizeof(ngx_http_mytest_conf_t));if (mycf == NULL) {return NULL;}/* 以下簡(jiǎn)單的硬編碼ngx_http_upstream_conf_t結(jié)構(gòu)中的各成員,如超時(shí)事件,都設(shè)為1分鐘,這也是HTTP反向代理模塊的默認(rèn)值*/mycf->upstream.connect_timeout = 60000;mycf->upstream.send_timeout = 60000;mycf->upstream.read_timeout = 60000;mycf->upstream.store_access = 0600;/* 實(shí)際上,buffering已經(jīng)決定了將以固定大小的內(nèi)存作為緩沖區(qū)來轉(zhuǎn)發(fā)上游的響應(yīng)包體,這塊固定緩沖區(qū)的大小就是buffer_size。如果buffering為1,就會(huì)使用更多的內(nèi)存緩存來不及發(fā)往下游的響應(yīng)。例如,最多使用bufs.num個(gè)緩沖區(qū)且每個(gè)緩沖區(qū)大小為bufs.size。另外,還會(huì)使用臨時(shí)文件,臨時(shí)文件的最大長度為max_temp_file_size */mycf->upstream.buffering = 0;mycf->upstream.bufs.num = 8;mycf->upstream.bufs.size = ngx_pagesize;mycf->upstream.buffer_size = ngx_pagesize;mycf->upstream.busy_buffers_size = 2*ngx_pagesize;mycf->upstream.temp_file_write_size = 2 * ngx_pagesize;mycf->upstream.max_temp_file_size = 1024 * 1024 * 1024;/* upstream模塊要求hide_headers成員必須要初始化(upstream在解析完上游服務(wù)器返回的包頭時(shí),會(huì)調(diào)用ngx_http_upstream_process_headers方法按照hide_headers成員將本應(yīng)轉(zhuǎn)發(fā)給下游的一些HTTP頭部隱藏),這里將它賦為NGX_CONF_UNSET_PTR,這是為了在merge合并配置項(xiàng)方法中使用upstream模塊提供的ngx_http_upstream_hide_headers_hash方法初始化hide_headers成員 */mycf->upstream.hide_headers = NGX_CONF_UNSET_PTR;mycf->upstream.pass_headers = NGX_CONF_UNSET_PTR:retrurn mycf; }static char *ngx_http_mytest_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) {ngx_http_mytest_conf_t *prev = (ngx_http_mytest_conf_t *)parent;ngx_http_mytest_conf_t *conf = (ngx_http_mytest_conf_t *)child;ngx_hash_init_t hash;hash.max_size = 100;hash.bucket_size = 1024;hash.name = "proxy_headers_hash";if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, &prev->upstream, ngx_http_proxy_hide_headers, &hash) != NGX_OK) {return NGX_CONF_ERROR;}return NGX_CONF_OK; }

? 本例必須要使用上下文才能正確地解析upstream上游服務(wù)器的響應(yīng)包,因?yàn)閡pstream模塊每次接收到一段TCP流時(shí)都會(huì)回調(diào)mytest模塊實(shí)現(xiàn)的process_header方法解析,這樣就需要有一個(gè)上下文保存解析狀態(tài)。

? ? ? (2) 在create_request方法中構(gòu)造請(qǐng)求

? ? ? ? ?下面方法用于創(chuàng)建發(fā)送給上游服務(wù)器的HTTP請(qǐng)求,upstream模塊將會(huì)回調(diào)它,實(shí)現(xiàn)如下:

static ngx_int_t mytest_upstream_create_request(ngx_http_request_t *r) {/* 在發(fā)往google上游服務(wù)器的請(qǐng)求很簡(jiǎn)單,就是模仿正常的搜索請(qǐng)求,以/search?q=...的URL來發(fā)起搜索請(qǐng)求。backendQueryLine中的%V等轉(zhuǎn)化格式的用法*/static ngx_str_t backendQueryLine = ngx_string("GET /search?q=%V HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n");ngx_int_t queryLineLen = backendQueryLine.len + r->args.len - 2;/* 必須在內(nèi)存池中申請(qǐng)內(nèi)存,這有以下兩點(diǎn)好處:一個(gè)好處是,在網(wǎng)絡(luò)情況不佳的情況下,向上游服務(wù)器發(fā)送請(qǐng)求時(shí),可能需要epoll多次調(diào)度send才能發(fā)送完成,這時(shí)必須保證這段內(nèi)存不會(huì)被釋放;另一個(gè)好處是,在請(qǐng)求結(jié)束時(shí),這段內(nèi)存會(huì)被自動(dòng)釋放,減低內(nèi)存泄露的可能性*/ngx_buf_t *b = ngx_create_temp_buf(r->pool, queryLineLen);if (b == NULL) {return NGX_ERROR;}//last要指向請(qǐng)求的末尾b->last = b->pos + queryLineLen;//作用相當(dāng)于snprintfngx_snprintf(b->pos, queryLineLen, (char *)backendQueryLine.data, &r->args);/* r->upstream->request_bufs是一個(gè)ngx_chain_t結(jié)構(gòu),它包含著要發(fā)送給上游服務(wù)器的請(qǐng)求 */r->upstream->request_bufs = ngx_alloc_chain_link(r->pool);if (r->upstream->request_bufs == NULL) return NGX_ERROR;//request_bufs在這里只包含1個(gè)ngx_buf_t緩沖區(qū)r->upstream->request_bufs->buf = b;r->upstream->request_bufs->next = NULL;r->upstream->request_sent = 0;r->upstream->header_sent = 0;// header_hash不可以為0r->header_hash = 1;return NGX_OK; }

? ? ? ? ? (3) 在process_header方法中解析包頭

? ? ? ? ? ? ?process_header負(fù)責(zé)解析上游服務(wù)器發(fā)來的基于TCP的包頭,在本例中,就是解析HTTP響應(yīng)行和HTTP頭部,因此,這里使用mytest_process_status_line方法解析HTTP響應(yīng)行,使用mytest_upstream_process_header方法解析http響應(yīng)頭部。

static ngx_int_t mytest_process_status_line(ngx_http_request_t *r) {size_t len;ngx_int_t rc;ngx_http_upstream_t *u;//上下文中才會(huì)保存多次解析HTTP響應(yīng)行的狀態(tài),下面首先取出請(qǐng)求的上下文ngx_http_mytest_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_mytest_module);if (ctx == NULL) {return NGX_ERROR;}u = r->upstream;/* HTTP框架提供的ngx_http_parse_status_line方法可以解析HTTP響應(yīng)行,它的輸入就是收到的字符流和上下文中的ngx_http_status_t結(jié)構(gòu) */rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status);//返回NGX_AGAIN時(shí),表示還沒有解析出完整的HTTP響應(yīng)行,需要接受更多的字符流再進(jìn)行解析if (rc == NGX_AGAIN) {return rc;}//返回NGX_ERROR時(shí),表示沒有接收到合法的HTTP響應(yīng)行if (rc == NGX_ERROR) {ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,"upstream sent no valid HTTP/1.0 header");r->http_version = NGX_HTTP_VERSION_9;u->state->status = NGX_HTTP_OK;return NGX_OK;}/* 以下表示在解析到完整的HTTP響應(yīng)行時(shí),會(huì)做一些簡(jiǎn)單的賦值操作,將解析出的信息設(shè)置到r->upstream->headers_in結(jié)構(gòu)體中。當(dāng)upstream解析完所有的包頭時(shí),會(huì)把headers_in中的成員設(shè)置到 將要向下游發(fā)送的r->headers_out結(jié)構(gòu)體中,也就是說,現(xiàn)在用戶向headers_in中設(shè)置的信息,最終都會(huì)發(fā)往下游客戶端。為什么不直接設(shè)置r->headers_out而要多此一舉呢?因?yàn)閡pstream希望能夠按照ngx_http_upstream_conf_t配置結(jié)構(gòu)體中的hide_headers等成員對(duì)發(fā)往下游的響應(yīng)頭部做統(tǒng)一處理 */if (u->state) {u->state->status = ctx->status.code;}u->headers_in.status_n = ctx->status.code;len = ctx->status.end - ctx->status.start;u->headers_in.status_line.len = len;u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);if (u->headers_in.status_line.data == NULL) {return NGX_ERROR;}ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len);/* 下一步將開始解析HTTP頭部。設(shè)置process_header回調(diào)方法為mytest_upstream_process_header,之后再收到的新字符流將由mytest_upstream_process_header解析 */u->process_header = mytest_upstream_process_header;/* 如果本次收到的字符流除了HTTP響應(yīng)行外,還有多余的字符,那么將由mytest_upstream_process_header方式解析 */return mytest_upstream_process_header(r); }

? mytest_upstream_process_header方法可以解析HTTP響應(yīng)頭部,而這里只是簡(jiǎn)單地把上游服務(wù)器發(fā)送的HTTP頭部添加到了請(qǐng)求r->upstream->headers_in.headers鏈表中。如果有需要特殊處理的HTTP頭部,那么應(yīng)該在mytest_upstream_process_header方法中進(jìn)行。

static ngx_int_t mytest_upstream_process_header(ngx_http_request_t *r) {ngx_int_t rc;ngx_table_elt_t *h;ngx_http_upstream_header_t *hh;ngx_http_upstream_main_conf_t *umcf;/* 這里將upstream模塊配置項(xiàng)ngx_http_upstream_main_conf_t取出來,目的只有一個(gè),就是對(duì)將要轉(zhuǎn)發(fā)給下游客戶端的HTTP響應(yīng)頭部進(jìn)行統(tǒng)一處理。該結(jié)構(gòu)體中存儲(chǔ)了需要進(jìn)行統(tǒng)一處理的HTTP頭部名稱和回調(diào)方法 */umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);//循環(huán)地解析所有的HTTP頭部for (;;) {/* HTTP框架提供了基礎(chǔ)性的ngx_http_parse_haeder_line方法,它用于解析HTTP頭部 */rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);//返回NGX_OK時(shí),表示解析出一行HTTP頭部if (rc == NGX_OK) {// 向headers_in.headers這個(gè)ngx_list_t鏈表中添加HTTP頭部h = ngx_list_push(&r->upstream->headers_in.headers);if (h == NULL) {return NGX_ERROR;}//下面開始構(gòu)造剛剛添加到headers鏈表中的HTTP頭部h->hash = r->header_hash;h->key.len = r->header_name_end - r->header_name_start;h->value.len = r->header_end - r->header_start;//必須在內(nèi)存池中分配存放HTTP頭部的內(nèi)存空間h->key.data = ngx_pnalloc(r->pool, h->key.len + 1 + h->value.len + 1 + h->key.len);if (h->key.data == NULL) {return NGX_ERROR;}h->value.data = h->key.data + h->key.len + 1;h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;ngx_memcpy(h->key.data, r->header_name_start, h->key.len);h->key.data[h->key.len] = '\0';ngx_memcpy(h->value.data, r->header_start, h->value.len);h->value.data[h->value.len] = '\0';if (h->key.len == r->lowcase_index) {ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);} else {ngx_strlow(h->lowcase_key, h->key.data, h->key.len);}//upstream模塊會(huì)對(duì)一些HTTP頭部做特殊處理hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len);if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {return NGX_ERROR;}continue;}/* 返回NGX_HTTP_PARSE_HEADER_DONE時(shí),表示響應(yīng)中所有的HTTP頭部解析完畢,接下來在接收到的都將是HTTP包體 */if (rc == NGX_HTTP_PARSE_HEADER_DONE) {/* 如果之前解析HTTP頭部時(shí)沒有發(fā)現(xiàn)server和date頭部,那么下面會(huì)根據(jù)HTTP協(xié)議規(guī)范添加這兩個(gè)頭部 */if (r->upstream->headers_in.server == NULL) {h = ngx_list_push(&r->upstream->headers_in.headers);if (h == NULL) {return NGX_ERROR;}h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');ngx_str_set(&h->key, "Date");ngx_str_null(&h->value);h->lowcase_key = (u_char *) "date";}return NGX_OK;}/* 如果返回NGX_AGAIN, 則表示狀態(tài)機(jī)還沒有解析到完整的HTTP頭部,此時(shí)要求upstream模塊繼續(xù)接收新的字符流,然后交由process_header回調(diào)方法解析 */if (rc == NGX_AGAIN) {return NGX_AGAIN;}//其他返回值都是非法的ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "upstream sent invalid header");return NGX_HTTP_UPSTREAM_INVALID_HEADER;} }

? ?當(dāng)mytest_upstream_process_header返回NGX_OK后,upstream模塊開始把上游的包體(如果有的話)直接轉(zhuǎn)發(fā)到下游客戶端。

? ? ? ? ? (4) 在finalize_request方法中釋放資源

? ? ? ? ? ? ? ? 當(dāng)請(qǐng)求結(jié)束時(shí),將會(huì)回調(diào)finalize_request方法,如果我們希望此時(shí)釋放資源,如打開的句柄等,那么可以把這樣的代碼添加到finalize_request方法中。本例中定義了mytest_upstream_finalize_request方法,由于我們沒有任何需要釋放的資源,所以該方法沒有完成任何實(shí)際工作,只是因?yàn)閡pstream模塊要求必須實(shí)現(xiàn)finalize_request回調(diào)方法。

? ? ? ?? ?(5) 在ngx_http_mytest_handler方法中啟動(dòng)upstream

? ? ? ? ? ? ? ? ?在開始介入處理客戶端請(qǐng)求的ngx_http_mytest_handler方法中啟動(dòng)upstream機(jī)制,而何時(shí)會(huì)結(jié)束,則視Nginx與上游的google服務(wù)器間的通信而定。

static ngx_int_t ngx_http_mytest_handler(ngx_http_request_r *r) {//首先建立HTTP上下文結(jié)構(gòu)體ngx_http_mytest_ctx_tngx_http_mytest_ctx_t *myctx = ngx_http_get_module_ctx(r, ngx_http_mytest_module);if (myctx == NULL) {myctx = ngx_palloc(r->pool, sizeof(ngx_http_mytest_ctx_t));if (myctx == NULL) {return NGX_ERROR;}//得到配置結(jié)構(gòu)體ngx_http_mytest_conf_tngx_http_mytest_conf_t *mycf = (ngx_http_mytest_conf_t *)ngx_http_get_module_loc_conf(r, ngx_http_mytest_module);ngx_http_upstream_t *u = r->upstream;//這里用配置文件中的結(jié)構(gòu)體來賦給r->upstream->conf成員u->conf = &mycf->upstream;//決定轉(zhuǎn)發(fā)包體時(shí)使用的緩沖區(qū)u->buffering = mycf->upstream.buffering;//以下代碼開始初始化resolved結(jié)構(gòu)體,用來保存上游服務(wù)器的地址u->resolved = (ngx_http_upstream_resolved_resolved_t *)ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));if (u->resolved == NULL) {ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_pcalloc resolved error. %s.", strerror(errno));return NGX_ERROR;}//這里的上游服務(wù)器就是www.google.comstatic struct sockaddr_in backendSockAddr;struct hostent *pHost = gethostbyname((char *) "www.google.com");if (pHost == NULL) {ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "gethostbyname fail. %s", strerror(errno));return NGX_ERROR;}//訪問上游服務(wù)器的80端口backendSockAddr.sin_family = AF_INET;backendSockAddr.sin_port = htons((in_port_t) 80);char *pDmsIP = inet_ntoa(*(struct in_addr *) (pHost->h_addr_list[0]));backendSockAddr.sin_addr.s_addr = inet_addr(pDmsIP);myctx->backendServer.data = (u_char *)pDmsIP;myctx->backendServer.len = strlen(pDmsIP);//將地址設(shè)置到resolved成員中u->resolved->sockaddr = (struct sockaddr *)&backendSockAddr;u->resolved->socklen = sizeof(struct sockaddr_in);u->resolved->naddrs = 1;// 設(shè)置3個(gè)必須實(shí)現(xiàn)的回調(diào)方法u->create_request = mytest_upstream_create_request;u->process_header = mytest_process_status_line;u->finalize_request = mytest_upstream_finalize_request;//這里必須將count成員加1r->main->count++;//啟動(dòng)upstreamngx_http_upstream_init(r);//必須返回NGX_DONEreturn NGX_DONE;}

? ? ? ? ? ?到此為止,高性能地訪問第三方服務(wù)的upstream例子就介紹完了。在本例中,可以完全異步地訪問第三方服務(wù),并發(fā)訪問數(shù)也只會(huì)受制于物理內(nèi)存的大小,完全可以輕松達(dá)到幾十萬的并發(fā)TCP連接。

50.使用subrequest的方式只需要完成以下4步操作即可:

? ? 1) 在nginx.conf文件中配置好子請(qǐng)求的處理方式;

? ? 2) 啟動(dòng)subrequest子請(qǐng)求;

? ? 3) 實(shí)現(xiàn)子請(qǐng)求執(zhí)行結(jié)束時(shí)的回調(diào)方法;

? ? 4) 實(shí)現(xiàn)父請(qǐng)求被激活時(shí)的回調(diào)方法。

51.ngx_http_subrequest的定義:

ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,ngx_http_post_subrequest_t *ps, ngx_uint_t flags);

? ? ?1) ngx_http_request_t *r

? ? ? ? ? ngx_http_request_t *r 是當(dāng)前的請(qǐng)求,也就是父請(qǐng)求。

? ? ? 2) ngx_str_t *uri

? ? ? ? ? ngx_str_t *uri是子請(qǐng)求的URI,它對(duì)究竟選用nginx.conf配置文件中的哪個(gè)模塊來處理子請(qǐng)求起決定性作用。

? ? ? 3) ngx_str_t *args

? ? ? ? ? ngx_str_t *args是子請(qǐng)求的URI參數(shù),如果沒有參數(shù),可以傳送NULL空指針。

? ? ? ?4) ngx_http_request_t **psr

? ? ? ? ? psr是輸出參數(shù)而不是輸入?yún)?shù),它將把ngx_http_subrequest生成的子請(qǐng)求傳出來。

? ? ? ? 5) ngx_http_post_subrequest_t *ps

? ? ? ? ? 這里傳入創(chuàng)建的ngx_http_post_subrequest_t結(jié)構(gòu)體地址,它指出子請(qǐng)求結(jié)束時(shí)必須回調(diào)的處理方法。

? ? ? ? ?6) ngx_uint_t flags

? ? ? ? ? ?flag的取值范圍包括: (1) 0.在沒有特殊需求的情況下都應(yīng)該填寫它; (2) NGX_HTTP_SUBREQUEST_IN_MEMORY。這個(gè)宏會(huì)將子請(qǐng)求的subrequest_in_memory標(biāo)志位置為1,這意味著如果子請(qǐng)求使用upstream訪問上游服務(wù)器,那么上游服務(wù)器的響應(yīng)都將會(huì)在內(nèi)存中處理;(3) NGX_HTTP_SUBREQUEST_WAITED。這個(gè)宏會(huì)將子請(qǐng)求的waited標(biāo)志位置為1,當(dāng)子請(qǐng)求提前結(jié)束時(shí),有個(gè)done標(biāo)志位置為1,但目前HTTP框架并沒有針對(duì)這兩個(gè)標(biāo)志位做任何實(shí)質(zhì)性處理。注意,flag是按比特位操作的,這樣可以同時(shí)包含上述3個(gè)值。

? ? ? ? ?7) 返回值

? ? ? ? ? ? 返回NGX_OK表示成功建立子請(qǐng)求;返回NGX_ERROR表示建立子請(qǐng)求失敗。

52.如何啟動(dòng)subrequest

? ? ?處理父請(qǐng)求的過程中會(huì)創(chuàng)建子請(qǐng)求,在父請(qǐng)求的處理方法返回NGX_DONE后,HTTP框架會(huì)開始執(zhí)行子請(qǐng)求

? ? ??

? ? ? ?上圖中的步驟如下:

? ? ? ?1) Nginx主循環(huán)中會(huì)定期地調(diào)用事件模塊,檢查是否有網(wǎng)絡(luò)事件發(fā)生;

? ? ? ?2) 事件模塊發(fā)現(xiàn)這個(gè)請(qǐng)求的回調(diào)方法屬于HTTP框架,交由HTTP框架來處理請(qǐng)求。

? ? ? ?3) 根據(jù)解析完的URI來決定使用哪個(gè)location下的模塊來處理這個(gè)請(qǐng)求。

? ? ? ?4) 調(diào)用mytest模塊的ngx_http_mytest_handler方法處理這個(gè)請(qǐng)求。

? ? ? ?5) 設(shè)置subrequest子請(qǐng)求的URI及回調(diào)方法。

? ? ? ?6) 調(diào)用ngx_http_subrequest方法創(chuàng)建子請(qǐng)求。

? ? ? ?7) 創(chuàng)建的子請(qǐng)求會(huì)添加到原始請(qǐng)求的posted_requests鏈表中,這樣保證第10步時(shí)會(huì)在父請(qǐng)求返回NGX_DONE的情況下開始執(zhí)行子請(qǐng)求。

? ? ? ?8) ngx_http_subrequest方法執(zhí)行完畢,子請(qǐng)求創(chuàng)建成功。

? ? ? ?9) ngx_http_mytest_handler方法執(zhí)行完畢,返回NGX_DONE,這樣父請(qǐng)求不會(huì)被銷毀,將等待以后的再次激活。

? ? ? ?10) HTTP框架執(zhí)行完當(dāng)前請(qǐng)求(父請(qǐng)求)后,檢查posted_requests鏈表中是否還有子請(qǐng)求,如果存在子請(qǐng)求,則調(diào)用子請(qǐng)求的write_event_handler方法。

? ? ? ? 11) 根據(jù)子請(qǐng)求的URI(第5步中建立),檢查nginx.conf文件中所有的location配置,確定應(yīng)由哪個(gè)模塊來執(zhí)行子請(qǐng)求。在本章的例子中,子請(qǐng)求是交由反向代理模塊執(zhí)行的。

? ? ? ? 12) 調(diào)用反向代理模塊的入口方法ngx_http_proxy_handler來處理子請(qǐng)求。

? ? ? ? ?13) 由于反向代理模塊使用了upstream機(jī)制,所以它也要通過許多次的異步調(diào)用才能完整地處理完子請(qǐng)求,這時(shí)它的入口方法會(huì)返回NGX_DONE.

? ? ? ? ?14) 再次檢查是否還有子請(qǐng)求,這時(shí)會(huì)發(fā)現(xiàn)已經(jīng)沒有子請(qǐng)求需要執(zhí)行了。當(dāng)然,子請(qǐng)求可以繼續(xù)建立新的子請(qǐng)求,只是這里的反向代理模塊不會(huì)這樣做。

? ? ? ? ?15) 當(dāng)?shù)?步中的網(wǎng)絡(luò)讀取事件處理完畢后,交還控制權(quán)給事件模塊。

? ? ? ? ?16) 當(dāng)本輪網(wǎng)絡(luò)事件處理完畢后,交還控制權(quán)給Nginx主循環(huán)。

53.如何轉(zhuǎn)發(fā)多個(gè)子請(qǐng)求的響應(yīng)包體

? ? ?每個(gè)請(qǐng)求的ngx_http_request_t結(jié)構(gòu)體中都有一個(gè)postponed成員:

struct ngx_http_request_s {...ngx_http_postponed_request_t *postponed;... } 它實(shí)際上是一個(gè)鏈表: typedef struct ngx_http_postponed_request_s ngx_http_postponed_request_t; struct ngx_http_postponed_request_s {ngx_http_request_t *request;ngx_chain_t *out;ngx_http_postponed_request_t *next; };

? ? ? ? 多個(gè)ngx_http_postponed_request_t之間使用next指針連接成一個(gè)單向鏈表。ngx_http_postponed_request_t中的out成員是ngx_chain_t結(jié)構(gòu),它指向的是來自上游的、將要轉(zhuǎn)發(fā)給下游的響應(yīng)包體。

? ? ? ? 每當(dāng)使用ngx_http_output_filter方法(反向代理模塊也使用該方法轉(zhuǎn)發(fā)響應(yīng))向下游的客戶端發(fā)送響應(yīng)包體時(shí),都會(huì)調(diào)用到ngx_http_postpone_filter_module過濾模塊處理這段要發(fā)送的包體。

//這里的參數(shù)in就是將要發(fā)送給客戶端的一段包體 static ngx_int_t ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) {ngx_connection_t *c;ngx_http_postponed_request_t *pr;//c是Nginx與下游客戶端間的連接,c->data保存的是原始請(qǐng)求c = r->connection;//如果當(dāng)前請(qǐng)求r是一個(gè)子請(qǐng)求(因?yàn)閏->data指向原始請(qǐng)求)if (r != c->data) {/* 如果待發(fā)送的in包體不為空,則把in加到postponed鏈表中屬于當(dāng)前請(qǐng)求的ngx_http_postponed_request_t結(jié)構(gòu)體的out鏈表中,同時(shí)返回NGX_OK,這意味著本次不會(huì)把in包體發(fā)送客戶端*/if (in) {ngx_http_postpone_filter_add(r, in);return NGX_OK;}//如果當(dāng)前請(qǐng)求是子請(qǐng)求,而in包體又為空,那么直接返回即可return NGX_OK;}//如果postponed為空,表示請(qǐng)求r沒有子請(qǐng)求產(chǎn)生的響應(yīng)需要轉(zhuǎn)發(fā)if (r->postponed == NULL) {/* 直接調(diào)用下一個(gè)HTTP過濾模塊繼續(xù)處理in包體即可。如果沒有錯(cuò)誤的話,就會(huì)向下游客戶端發(fā)送響應(yīng) */if (in || c->buffered) {return ngx_http_next_filter(r->main, in);}return NGX_OK;}/* 至此,說明postponed鏈表中是有子請(qǐng)求產(chǎn)生的響應(yīng)需要轉(zhuǎn)發(fā)的,可以先把in包體加到待轉(zhuǎn)發(fā)響應(yīng)的末尾 */if (in) {ngx_http_postpone_filter_add(r, in);}//循環(huán)處理postponed鏈表中所有子請(qǐng)求待轉(zhuǎn)發(fā)的包體do {pr = r->postponed;/* 如果pr->request是子請(qǐng)求,則加入到原始請(qǐng)求的posted_requests隊(duì)列中,等待HTTP框架下次調(diào)用這個(gè)請(qǐng)求時(shí)再來處理 */if (pr->request) {r->postponed = pr->next;c->data = pr->request;return ngx_http_post_request(pr->request, NULL);}//調(diào)用下一個(gè)HTTP過濾模塊轉(zhuǎn)發(fā)out鏈表中保存的待轉(zhuǎn)發(fā)的包體if (pr->out == NULL) {} else {if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) {return NGX_ERROR;}}//遍歷完postponed鏈表r->postponed = pr->next;} while (r->postponed);return NGX_OK; }

? ? ? ? ?

54.子請(qǐng)求在結(jié)束前會(huì)回調(diào)在ngx_http_post_subrequest_t中實(shí)現(xiàn)的handler方法,在這個(gè)handler方法中,又設(shè)置了父請(qǐng)求被激活后的執(zhí)行方法mytest_post_handler,流程如下:

? ? ? ? ? ? ? ??

? ?上圖中的步驟如下:

? ? 1) Nginx主循環(huán)中會(huì)定期地調(diào)用事件模塊,檢查是否有網(wǎng)絡(luò)事件發(fā)生。

? ? 2) 如果事件模塊檢測(cè)到連接關(guān)閉事件,而這個(gè)請(qǐng)求的處理方法屬于upstream模塊,則交由upstream模塊來處理請(qǐng)求。

? ? 3) upstream模塊開始調(diào)用ngx_http_upstream_finalize_request方法來結(jié)束upstream機(jī)制下的請(qǐng)求。

? ? 4) 調(diào)用HTTP框架提供的ngx_http_finalize_request方法來結(jié)束子請(qǐng)求。

? ? 5) ngx_http_finalize_request方法會(huì)檢查當(dāng)前的請(qǐng)求是否是子請(qǐng)求,如果是子請(qǐng)求,則會(huì)回調(diào)post_subrequest成員中的handler方法,也就是會(huì)調(diào)用mytest_subrequest_post_handler方法。

? ? 6) 在實(shí)現(xiàn)的子請(qǐng)求回調(diào)方法中,解析子請(qǐng)求返回的響應(yīng)包。注意,這時(shí)需要通過write_event_handler設(shè)置父請(qǐng)求被激活后的回調(diào)方法(因此此時(shí)父請(qǐng)求的回調(diào)方法已經(jīng)被HTTP框架設(shè)置為什么事情也不做的ngx_http_request_empty_handler方法).

? ? 7) 子請(qǐng)求的回調(diào)方法執(zhí)行完畢后,交由HTTP框架的ngx_http_finalize_request方法繼續(xù)向下執(zhí)行。

? ? 8) ngx_http_finalize_request方法執(zhí)行完畢。

? ? 9) HTTP框架如果發(fā)現(xiàn)當(dāng)前請(qǐng)求后還有父請(qǐng)求需要執(zhí)行,則調(diào)用父請(qǐng)求的write_event_handler回調(diào)方法。

? ? 10) 這里可以根據(jù)第6步中解析子請(qǐng)求響應(yīng)后的結(jié)果來構(gòu)造響應(yīng)包。

? ? ?11) 調(diào)用無阻塞的ngx_http_send_header、ngx_http_output_filter發(fā)送方法,向客戶端發(fā)送響應(yīng)包。

? ? ?12) 無阻塞發(fā)送方法會(huì)立刻返回,即使目前未發(fā)送完,Nginx之后也會(huì)異步地發(fā)送完所有的響應(yīng)包,然后再結(jié)束請(qǐng)求。

? ? ? 13) 父請(qǐng)求的回調(diào)方法執(zhí)行完畢。

? ? ? 14) 當(dāng)?shù)?步中的上游服務(wù)器連接關(guān)閉時(shí)間處理完畢后,交還控制權(quán)給事件模塊。

? ? ? 15) 當(dāng)本輪網(wǎng)絡(luò)事件處理完畢后,交還控制權(quán)給Nginx主循環(huán)。

55.subrequest是分解復(fù)雜請(qǐng)求的設(shè)計(jì)方法,派生出的子請(qǐng)求使用某些HTTP模塊基于upstream訪問第三方服務(wù)是最常見的用法,通過subrequest可以使Nginx在保持高并發(fā)的前提下處理復(fù)雜的業(yè)務(wù)。

56.

57.默認(rèn)即編譯進(jìn)Nginx的HTTP過濾模塊

默認(rèn)即編譯進(jìn)Nginx的HTTP過濾模塊功能
ngx_http_not_modified_filter_module僅對(duì)HTTP頭部進(jìn)行處理。在返回200成功時(shí),根據(jù)請(qǐng)求中If-Modified-Since或者If-Unmodified-Since頭部取得瀏覽器緩存文件的時(shí)間,再分析返回用戶文件的最后修改時(shí)間,以此決定是否直接發(fā)送304 Not Modified響應(yīng)給用戶
ngx_http_range_body_filter_module處理請(qǐng)求中的Range信息,根據(jù)Range中的要求返回文件的一部分給用戶
ngx_http_copy_filter_module僅對(duì)HTTP包體做處理。將用戶發(fā)送的ngx_chain_t結(jié)構(gòu)的HTTP包體復(fù)制到新的ngx_chain-t結(jié)構(gòu)中(都是各種指針的復(fù)制,不包括實(shí)際HTTP響應(yīng)內(nèi)容),后續(xù)的HTTP過濾模塊處理的ngx_chain-t類型的成員都是ngx_http_copy_filter_module模塊處理后的變量
ngx_http_headers_filter_module僅對(duì)HTTP頭部做處理。允許通過修改nginx.conf配置文件,在返回給用戶的響應(yīng)中添加任意的HTTP頭部
ngx_http_userid_filter_module僅對(duì)HTTP頭部做處理。這就是執(zhí)行configure命令時(shí)提到的http_userid_module模塊,它基于cookie提供了簡(jiǎn)單的認(rèn)證管理功能
ngx_http_charset_filter_module可以將文本類型返回給用戶的響應(yīng)包,按照nginx.conf中的配置重新進(jìn)行編碼,再返回給用戶
ngx_http_ssi_filter_module支持SSI(Server Side Include,服務(wù)器端嵌入)功能,將文件內(nèi)容包含到網(wǎng)頁中并返回給用戶
ngx_http_postpone_filter_module僅對(duì)HTTP包體做處理。它僅應(yīng)用于subrequest產(chǎn)生的子請(qǐng)求。它使得多個(gè)子請(qǐng)求同時(shí)向客戶端發(fā)送響應(yīng)時(shí)能夠有序,所謂的“有序”是指按照子請(qǐng)求的順序發(fā)送響應(yīng)
ngx_http_gzip_filter_module對(duì)特定的HTTP響應(yīng)包體(如網(wǎng)頁或者文本文件)進(jìn)行g(shù)zip壓縮,再把壓縮后的內(nèi)容返回給用戶
ngx_http_range_header_filter_module

支持range協(xié)議

ngx_http_chunked_filter_module支持chunk編碼
ngx_http_header_filter_module僅對(duì)HTTP頭部做處理。該過濾模塊將會(huì)把r->headers_out結(jié)構(gòu)體中的成員序列化為返回給用戶的HTTP響應(yīng)字符流,包括響應(yīng)行(如HTTP/1.1 200 OK)和響應(yīng)頭部,并通過調(diào)用ngx_http_write_filter_module過濾模塊中的過濾方法直接將HTTP包頭發(fā)送給客戶端
ngx_http_write_filter_module僅對(duì)HTTP包體做處理。該模塊負(fù)責(zé)向客戶端發(fā)送HTTP響應(yīng)

58.過濾模塊例子中,HTTP頭部處理方法的執(zhí)行活動(dòng)圖

?

59.過濾模塊例子中,HTTP包體處理方法的執(zhí)行活動(dòng)圖

? ?

60.參考另一篇:https://blog.csdn.net/zhangge3663/article/details/83180659

61.ngx_module_t接口及其對(duì)核心、事件、HTTP、mail等4類模塊ctx上下文成員的具體化

? ??

62.Nginx常用模塊及其之間的關(guān)系

? ??

63.傳統(tǒng)Web服務(wù)器和Nginx間的重要差別:前者是每個(gè)事件消費(fèi)者獨(dú)占一個(gè)進(jìn)程資源,后者的事件消費(fèi)者只是被事件分發(fā)者進(jìn)程短期調(diào)用而已。

64.在阻塞代碼段上按照下面4種方式來劃分階段:

? ?(1) 將阻塞進(jìn)程的方法按照相關(guān)的觸發(fā)事件分解為兩個(gè)階段

? ?(2) 將阻塞方法調(diào)用按照時(shí)間分解為多個(gè)階段的方法調(diào)用

? ?(3) 在“無所事事”且必須等待系統(tǒng)的響應(yīng),從而導(dǎo)致進(jìn)程空轉(zhuǎn)時(shí),使用定時(shí)器劃分階段

? ?(4) 如果阻塞方法完全無法繼續(xù)劃分,則必須使用獨(dú)立的進(jìn)程執(zhí)行這個(gè)阻塞方法

65.內(nèi)存池的設(shè)計(jì)

? ? 為了避免出現(xiàn)內(nèi)存碎片、減少向操作系統(tǒng)申請(qǐng)內(nèi)存的次數(shù)、降低各個(gè)模塊的開發(fā)復(fù)雜度,Nginx設(shè)計(jì)了簡(jiǎn)單的內(nèi)存池。這個(gè)內(nèi)存池沒有很復(fù)雜的功能:通常它不負(fù)責(zé)回收內(nèi)存池中已經(jīng)分配出的內(nèi)存。

? ? 通常每一個(gè)請(qǐng)求都有一個(gè)這種簡(jiǎn)易的獨(dú)立內(nèi)存池(Nginx為每一個(gè)TCP連接都分配一個(gè)內(nèi)存池,HTTP框架為每一個(gè)HTTP請(qǐng)求又分配了1個(gè)內(nèi)存池),而在請(qǐng)求結(jié)束時(shí)則會(huì)銷毀整個(gè)內(nèi)存池,把曾經(jīng)分配的內(nèi)存一次性歸還給操作系統(tǒng)。

66.Nginx核心的框架代碼一直圍繞著一個(gè)結(jié)構(gòu)體展開,它就是ngx_cycle_t。無論是master管理進(jìn)程、worker工作進(jìn)程還是cache manager(loader)進(jìn)程,每一個(gè)進(jìn)程都毫無例外地?fù)碛形ㄒ灰粋€(gè)ngx_cycle_t結(jié)構(gòu)體。

? ? 作為一個(gè)Web服務(wù)器,Nginx首先需要監(jiān)聽端口并處理其中的網(wǎng)絡(luò)事件。ngx_cycle_t對(duì)象中有一個(gè)動(dòng)態(tài)數(shù)組成員叫做listening,它的每一個(gè)數(shù)組元素都是ngx_listening_t結(jié)構(gòu)體,而每個(gè)ngx_listen_t結(jié)構(gòu)體又代表著Nginx服務(wù)器監(jiān)聽的一個(gè)端口。

typedef struct ngx_listening_s ngx_listening_t; struct ngx_listening_s {//socket套接字句柄ngx_socket_t fd;//監(jiān)聽sockaddr地址struct sockaddr *sockaddr;//sockaddr地址長度socklen_t socklen;/* 存儲(chǔ)IP地址的字符串a(chǎn)ddr_text最大長度,即它指定了addr_text所分配的內(nèi)存大小 */size_t addr_text_max_len;//以字符串形式存儲(chǔ)IP地址ngx_str_t addr_text;//套接字地址。例如,當(dāng)type是SOCK_STREAM時(shí),表示TCPint type;/* TCP實(shí)現(xiàn)監(jiān)聽時(shí)的backlog隊(duì)列,它表示允許正在通過三次握手建立TCP連接但還沒有任何進(jìn)程開始處理的連接最大個(gè)數(shù) */int backlog;//內(nèi)核中對(duì)于這個(gè)套接字的接收緩沖區(qū)大小int rcvbuf;//內(nèi)核中對(duì)于這個(gè)套接字的發(fā)送緩沖區(qū)大小int sndbuf;//當(dāng)新的TCP連接成功建立后的處理方法ngx_connection_handler_pt handler;/* 實(shí)際上框架并不使用servers指針,它更多的是作為一個(gè)保留指針,目前主要用于HTTP或者mail等模塊,用于保存當(dāng)前監(jiān)聽端口對(duì)應(yīng)著的所有主機(jī)名 */void *servers;//log和logp都是可用的日志對(duì)象的指針ngx_log_t log;ngx_lot_t *logp;//如果為新的TCP連接創(chuàng)建內(nèi)存池,則內(nèi)存池的初始大小應(yīng)該是pool_sizesize_t pool_size;/* TCP_DEFER_ACCEPT選項(xiàng)將在建立TCP連接成功且接收到用戶的請(qǐng)求數(shù)據(jù)后,才向?qū)ΡO(jiān)聽套接字感興趣的進(jìn)程發(fā)送事件通知,而連接建立成功后,如果post_accept_timeout秒后仍然沒有收到的用戶數(shù)據(jù),則內(nèi)核直接丟棄連接 */ngx_msec_t post_accept_timeout;/* 前一個(gè)ngx_listening_t 結(jié)構(gòu),多個(gè)ngx_listening_t結(jié)構(gòu)體之間由previous指針組成單鏈表 */ngx_listening_t *previous;//當(dāng)前監(jiān)聽句柄對(duì)應(yīng)著的ngx_connection_t結(jié)構(gòu)體ngx_connection_t *connection;/* 標(biāo)志位,為1則表示在當(dāng)前監(jiān)聽句柄有效,且執(zhí)行ngx_init_cycle時(shí)不關(guān)閉監(jiān)聽端口,為0時(shí)則正常關(guān)閉。該標(biāo)志位框架代碼會(huì)自動(dòng)設(shè)置 */unsigned open:1;/* 標(biāo)志位,為1表示使用已有的ngx_cycle_t來初始化新的ngx_cycle_t結(jié)構(gòu)體時(shí),不關(guān)閉原來打開的監(jiān)聽端口,這對(duì)運(yùn)行中升級(jí)程序很有用,remain為0時(shí),表示正常關(guān)閉曾經(jīng)打開的監(jiān)聽端口。該標(biāo)志位框架代碼會(huì)自動(dòng)設(shè)置,參見ngx_init_cycle方法 */unsigned remain:1;/* 標(biāo)志位,為1時(shí)表示跳過設(shè)置當(dāng)前ngx_listening_t結(jié)構(gòu)體中的套接字,為0時(shí)正常初始化套接字。該標(biāo)志位框架代碼會(huì)自動(dòng)設(shè)置 */unsigned ignore:1;//表示是否已經(jīng)綁定。實(shí)際上目前該標(biāo)志位沒有使用unsigned bound:1; /* 已經(jīng)綁定 *//* 表示當(dāng)前監(jiān)聽句柄是否來自前一個(gè)進(jìn)程(如升級(jí)Nginx程序),如果為1,則表示來自前一個(gè)進(jìn)程。一般會(huì)保留之前已經(jīng)設(shè)置好的套接字,不做改變 */unsigned inherited:1; /* 來自前一個(gè)進(jìn)程 *///目前未使用unsigned nonblocking_accept:1;//標(biāo)志位,為1時(shí)表示當(dāng)前結(jié)構(gòu)體對(duì)應(yīng)的套接字已經(jīng)監(jiān)聽unsigned listen:1;//表示套接字是否阻塞,目前該標(biāo)志位沒有意義unsigned nonblocking:1;//目前該標(biāo)志位沒有意義unsigned shared:1;//標(biāo)志位,為1時(shí)表示Nginx會(huì)將網(wǎng)絡(luò)地址轉(zhuǎn)變?yōu)樽址问降牡刂穟nsigned addr_ntop:1; };

67.Nginx框架是圍繞著ngx_cycle_t結(jié)構(gòu)體來控制進(jìn)程運(yùn)行的。

typedef struct ngx_cycle_s ngx_cycle_t; struct ngx_cycle_s {/* 保存著所有模塊存儲(chǔ)配置項(xiàng)的結(jié)構(gòu)體的指針,它首先是一個(gè)數(shù)組,每個(gè)數(shù)組成員又是一個(gè)指針,這個(gè)指針指向另一個(gè)存儲(chǔ)著指針的數(shù)組,因此會(huì)看到void **** */void ****conf_ctx;//內(nèi)存池ngx_pool_t *pool;/* 日志模塊中提供了生成基本ngx_log_t日志對(duì)象的功能,這里的log實(shí)際上是在還沒有執(zhí)行ngx_init_cycle方法前,也就是還沒有解析配置前,如果有信息需要輸出到日志,就會(huì)暫時(shí)使用log對(duì)象,它會(huì)輸出到屏幕。在ngx_init_cycle方法執(zhí)行后,將會(huì)根據(jù)nginx.conf配置文件中的配置項(xiàng),構(gòu)造出正確的日志文件,此時(shí)會(huì)對(duì)log重新賦值 */ngx_lot_t *log;/*由nginx.conf配置文件讀取到日志文件路徑后,將開始初始化error_log日志文件,由于log對(duì)象還在用于輸出日志到屏幕,這時(shí)會(huì)用new_log對(duì)象暫時(shí)性地替代log日志,待初始化成功后,會(huì)用new_log的地址覆蓋上面的log指針 */ngx_lot_t new_log;//與下面的files成員配合使用,指出files數(shù)組里元素的總數(shù)ngx_uint_t files_n;/* 對(duì)于poll、rtsig這樣的事件模塊,會(huì)以有效文件句柄數(shù)來預(yù)先建立這些ngx_connection_t結(jié)構(gòu)體,以加速事件的收集、分發(fā)。這時(shí)files就會(huì)保存所有ngx_connection_t的指針組成的數(shù)組,files_n就是指針的總數(shù),而文件句柄的值用來訪問files數(shù)組成員 */ngx_connection_t **files;//可用連接池,與free_connection_n配合使用ngx_connection_t *free_connections;//可用連接池中連接的總數(shù)ngx_uint_t free_connection_n;/* 雙向鏈表容器,元素類型是ngx_connection_t結(jié)構(gòu)體,表示可重復(fù)使用連接隊(duì)列 */ngx_queue_t reusable_connections_queue;/*動(dòng)態(tài)數(shù)組,每個(gè)數(shù)組元素存儲(chǔ)著ngx_listening_t成員,表示監(jiān)聽端口及相關(guān)的參數(shù) */ngx_array_t listening;/*動(dòng)態(tài)數(shù)組容器,它保存著Nginx所有要操作的目錄。如果有目錄不存在,則會(huì)視圖創(chuàng)建,而創(chuàng)建目錄失敗將會(huì)導(dǎo)致Nginx啟動(dòng)失敗。例如,上傳文件的臨時(shí)目錄也在pathes中,如果沒有權(quán)限創(chuàng)建,則會(huì)導(dǎo)致Nginx無法啟動(dòng) */ngx_array_t pathes;/* 單鏈表容器,元素類型是ngx_open_file_t結(jié)構(gòu)體,它表示Nginx已經(jīng)打開的所有文件。事實(shí)上,Nginx框架不會(huì)向open_files鏈表中添加文件,而是由對(duì)此感興趣的模塊向其中添加文件路徑名,Nginx框架會(huì)有ngx_init_cycle方法中打開這些文件 */ngx_list_t open_files;/* 單鏈表容器,元素的類型是ngx_shm_zone_t結(jié)構(gòu)體,每個(gè)元素表示一塊共享內(nèi)存*/ngx_list_t shared_memory;//當(dāng)前進(jìn)程中所有連接對(duì)象的總數(shù),與下面的connections成員配合使用ngx_uint_t connection_n;//指向當(dāng)前進(jìn)程中的所有連接對(duì)象,與connection_n配合使用ngx_connection_t *connections;//指向當(dāng)前進(jìn)程中的所有讀事件對(duì)象,connection_n同時(shí)表示所有讀事件的總數(shù)ngx_event_t *read_events;//指向當(dāng)前進(jìn)程中的所有寫事件對(duì)象,connection_n同時(shí)表示所有寫事件的總數(shù)ngx_event_t *write_events;/* 舊的ngx_cycle_t對(duì)象用于引用上一個(gè)ngx_cycle_t對(duì)象中的成員。例如ngx_init_cycle方法,在啟動(dòng)初期,需要建立一個(gè)臨時(shí)的ngx_cycle_t對(duì)象保存一些變量,再調(diào)用ngx_init_cycle方法時(shí)就可以把舊的ngx_cycle_t對(duì)象傳進(jìn)去,而這時(shí)old_cycle對(duì)象就會(huì)保存這個(gè)前期的ngx_cycle_t對(duì)象 */ngx_cycle_t *old_cycle;//配置文件相對(duì)于安裝目錄的路徑名稱ngx_str_t conf_file;/* Nginx處理配置文件時(shí)需要特殊處理的在命令行攜帶的參數(shù),一般是-g選項(xiàng)攜帶的參數(shù) */ngx_str_t conf_param;//Nginx配置文件所在目錄的路徑ngx_str_t conf_prefix;//Nginx安裝目錄的路徑ngx_str_t prefix;//用于進(jìn)程間同步的文件鎖名稱ngx_str_t lock_file;//使用gethostname系統(tǒng)調(diào)用得到的主機(jī)名ngx_str_t hostname; };

68.Nginx啟動(dòng)過程的流程圖

? ?

69.worker進(jìn)程正常工作、退出時(shí)的流程圖

? ??

70.master進(jìn)程不需要處理網(wǎng)絡(luò)事件,它不負(fù)責(zé)業(yè)務(wù)的執(zhí)行,只會(huì)通過管理worker等子進(jìn)程來實(shí)現(xiàn)重啟服務(wù)、平滑升級(jí)、更換日志文件、配置文件實(shí)時(shí)生效等功能。

71.epoll是目前Linux操作系統(tǒng)上最強(qiáng)大的事件管理機(jī)制。

72.首先,Nginx定義了一個(gè)核心模塊ngx_events_module,這樣在Nginx啟動(dòng)時(shí)會(huì)調(diào)用ngx_init_cycle方法解析配置項(xiàng),一旦在nginx.conf配置文件中找到ngx_events_module感興趣的“events{}”配置項(xiàng),ngx_events_module模塊就開始工作了。ngx_events_module模塊定義了事件類型的模塊,它的全部工作就是為所有的事件模塊解析"events{}"中的配置項(xiàng),同時(shí)管理這些事件模塊存儲(chǔ)配置項(xiàng)的結(jié)構(gòu)體。

? ? 其次,Nginx定義了一個(gè)非常重要的事件模塊ngx_event_core_module,這個(gè)模塊會(huì)決定使用哪種事件驅(qū)動(dòng)機(jī)制,以及如何管理事件。

? ? 最后,Nginx定義了一系列運(yùn)行在不同操作系統(tǒng)、不同內(nèi)核版本上的事件驅(qū)動(dòng)模塊,包括:ngx_epoll_module、ngx_kqueue_module、ngx_poll_module、ngx_select_module、ngx_devpoll_module、ngx_eventport_module、ngx_aio_module、ngx_rtsig_module和基于Windows的ngx_select_module模塊。在ngx_event_core_module模塊的初始化過程中,將會(huì)從以上模塊中選取一個(gè)作為Nginx進(jìn)程的事件驅(qū)動(dòng)模塊。

73.ngx_connection_t連接池示意圖

? ??

74.所有事件模塊配置項(xiàng)結(jié)構(gòu)體的指針是如何管理的。

??

75.ngx_event_core_module事件模塊啟動(dòng)時(shí)的工作流程

? ? ?

? 76.epoll在Linux內(nèi)核中申請(qǐng)了一個(gè)簡(jiǎn)易的文件系統(tǒng),把原來的一個(gè)select或者poll調(diào)用分成了3個(gè)部分:調(diào)用epoll_create建立1個(gè)epoll對(duì)象(在epoll文件系統(tǒng)中給這個(gè)句柄分配資源)、調(diào)用epoll_ctx向epoll對(duì)象中添加這100萬個(gè)連接的套接字、調(diào)用epoll_wait收集發(fā)生事件的連接。

77.ngx_event_accept方法建立新連接的流程

? ?

78.ngx_process_events_and_times方法中的事件框架處理流程

? ?

79.解析server{}塊內(nèi)配置項(xiàng)的流程

? ?

80.解析location{}配置塊的流程

? ??

81.HTTP框架的初始化流程

? ??

82.接收、解析HTTP請(qǐng)求行的流程圖

? ??

83.ngx_http_process_request_headers方法接收HTTP頭部的流程圖

? ??

84.ngx_http_process_request處理HTTP請(qǐng)求的流程圖

? ??

85.ngx_http_core_run_phases方法的執(zhí)行流程

? ??

86.ngx_http_request_handler方法的執(zhí)行流程

? ??

87.ngx_http_read_client_request_body方法的流程圖

? ??

88.upstream機(jī)制的場(chǎng)景示意圖

? ??

89.ngx_http_upstream_connect方法的流程圖

? ??

90.ngx_http_upstream_send_request方法的流程圖

? ? ?

91.郵件代理功能的示意序列圖

? ??

? ?從網(wǎng)絡(luò)通信的角度來看,Nginx實(shí)現(xiàn)郵件代理功能時(shí)會(huì)把一個(gè)請(qǐng)求分為以下4個(gè)階段。

? ?1) 接收并解析客戶端初始請(qǐng)求的階段;

? ?2) 向認(rèn)證服務(wù)器驗(yàn)證請(qǐng)求合法性,并獲取上游郵件服務(wù)器地址的階段;

? ?3) Nginx根據(jù)用戶信息多次與上游郵件服務(wù)器交互驗(yàn)證合法性的階段;

? ?4) Nginx在客戶端與上游郵件服務(wù)器間純粹透?jìng)鱐CP流的階段。

92.初始化郵件請(qǐng)求的流程

? ??

93.啟動(dòng)郵件認(rèn)證、向認(rèn)證服務(wù)器發(fā)起連接的流程

? ?

94.Nginx框架使用了3種傳遞消息傳遞方式:共享內(nèi)存、套接字、信號(hào)。Nginx各進(jìn)程間共享數(shù)據(jù)的主要方式就是使用共享內(nèi)存.

總結(jié)

以上是生活随笔為你收集整理的《深入理解NGINX 模块开发与架构解析》之摘抄学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

成人av在线看 | 麻豆国产精品永久免费视频 | 伊人五月综合 | 欧美另类交在线观看 | av成人免费在线 | av在线a | 成人av av在线 | 中文字幕 国产视频 | 美女又爽又黄 | 成人av中文字幕 | 国产精品久久久久影院 | 国产69精品久久99的直播节目 | 国产高清久久久久 | 亚洲天堂在线观看完整版 | 丝袜一区在线 | 国产 色 | 日本午夜免费福利视频 | 综合中文字幕 | 美女网站色在线观看 | 国产一级一片免费播放放 | 国产精品日韩久久久久 | 日韩毛片在线免费观看 | 狠狠色狠狠色综合系列 | 久久av高清 | 日韩理论片在线 | 色在线网| 成人精品国产免费网站 | 欧洲精品久久久久毛片完整版 | 波多野结衣在线中文字幕 | 伊人国产在线播放 | 日操干| 欧美超碰在线 | 四虎成人精品永久免费av | 免费看特级毛片 | 草久在线观看视频 | 亚洲国产日韩一区 | 国产高清av免费在线观看 | 亚洲免费av一区二区 | 亚洲欧美日韩中文在线 | 四虎国产精品免费观看视频优播 | 精品久久久久久久久亚洲 | 日本久久久影视 | www日韩精品 | 久久最新 | 国产一区成人 | 黄色av电影免费观看 | 91精品导航| 成人免费在线播放 | 美女在线免费观看视频 | 日韩高清一二三区 | 永久黄网站色视频免费观看w | 亚洲国产精彩中文乱码av | 欧美成亚洲 | 国产综合激情 | av官网 | 亚洲精品综合久久 | 中文字幕二区三区 | 免费精品国产 | 国产精品久久久久久一区二区三区 | 97av影院| 成 人 黄 色 视频 免费观看 | 国产品久精国精产拍 | 久久99电影 | 狠狠操电影网 | 免费在线观看视频a | 日韩免费视频在线观看 | 婷婷av网站 | 色香蕉网 | 免费男女羞羞的视频网站中文字幕 | 日韩av电影手机在线观看 | 成人在线免费视频 | 99亚洲精品视频 | 日韩中文字幕在线 | 欧美日韩视频一区二区 | 日本在线观看中文字幕无线观看 | 五月天综合婷婷 | 中文在线免费看视频 | 成年人在线观看视频免费 | 免费看一级 | 免费在线播放黄色 | a久久久久久 | 欧美动漫一区二区三区 | 午夜精品一区二区三区四区 | 国产成人精品电影久久久 | 久久99精品久久只有精品 | 免费观看成人 | 国产精品久久久久久久久久久久午夜 | 韩国精品视频在线观看 | 久精品视频免费观看2 | 日本中文字幕系列 | 亚洲精品国产品国语在线 | 五月婷婷.com | 丁香婷婷深情五月亚洲 | 丁香六月av | 中文字幕中文字幕在线中文字幕三区 | 亚洲va欧美va人人爽春色影视 | 国产精品久久久久久久久岛 | 最新高清无码专区 | 亚洲五月 | 欧美日韩国产综合网 | 欧美久久九九 | 国产精品一区二区麻豆 | 国产亚洲精品成人 | 手机av网站 | 久久精品国产精品亚洲 | 久久久久久免费网 | 91视频a| 免费的成人av | 亚洲午夜精品久久久 | 色www精品视频在线观看 | av电影一区二区 | 国产免费三级在线观看 | 久久久精品欧美一区二区免费 | 九九久久影院 | 人人干天天干 | 久久激情小说 | 操操爽 | 国产精品美女久久久久久久 | 91精品在线免费观看视频 | 久久福利综合 | 国产高清在线视频 | 日韩高清一 | 国产中文字幕视频 | 亚洲成av | 亚洲精品视频国产 | 91丨九色丨高潮丰满 | 日韩视频一区二区在线观看 | 国产视频在 | av在线免费在线观看 | 久久九九久久精品 | 国产成人精品av | 欧美日韩综合在线观看 | 久久国内视频 | 婷婷六月天天 | 最近中文字幕免费av | 欧洲激情综合 | 国产精品va在线观看入 | 国产美女精品人人做人人爽 | 国产主播大尺度精品福利免费 | 97在线免费| 欧美性护士 | 国产超碰97 | 色视频网页 | 国产高清av免费在线观看 | 狠狠搞,com| 四虎www | 国产久视频 | 成人av网站在线 | 天天操天天能 | 波多野结衣理论片 | 久久夜夜爽 | 伊人五月综合 | 国产成人中文字幕 | 亚洲精品国产第一综合99久久 | 黄色视屏免费在线观看 | 免费成人结看片 | 精品国产中文字幕 | 婷婷久久一区 | 色婷婷精品大在线视频 | 麻豆视频在线播放 | 亚洲手机天堂 | 91黄色小网站 | 女人18片毛片90分钟 | 免费观看91视频大全 | 在线观看av中文字幕 | 九九九热 | 亚洲精品88欧美一区二区 | 亚洲国产精品va在线看黑人动漫 | 国产黄色片免费看 | 字幕网av| 在线观看国产高清视频 | 成人一区二区在线 | 91视频com| 91av资源在线 | 欧美一区影院 | 中文字幕你懂的 | 91麻豆产精品久久久久久 | 久久久国产影视 | 91精品久久久久久久久久入口 | 五月天网页 | 日本二区三区在线 | 正在播放国产一区二区 | 在线激情影院一区 | 国产专区精品视频 | 黄色av大片| 亚洲成人资源在线观看 | 天天综合网天天 | 色香com. | 美女视频免费精品 | 在线看中文字幕 | 插综合网| 人人射人人爱 | 91九色精品女同系列 | 毛片美女网站 | 人人爱爱人人 | 中文字幕在线色 | 亚洲成人家庭影院 | 久久精品欧美一区 | 美女视频一区二区 | 天天拍天天爽 | 91九色精品女同系列 | 亚洲精品99久久久久中文字幕 | 国产精品麻豆一区二区三区 | 五月天六月色 | 米奇影视7777 | 国产资源在线播放 | 久九视频 | 亚洲精品乱码久久久一二三 | 国产精品视频99 | www.午夜色.com | 91少妇精拍在线播放 | 粉嫩av一区二区三区四区 | av成人免费网站 | 中文字幕亚洲欧美 | 黄色一级网| 又粗又长又大又爽又黄少妇毛片 | 国产精品美女久久久久久网站 | 97在线观看视频免费 | 久久久精品亚洲 | 日韩影视大全 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产精品久久久久久久久婷婷 | 日韩欧三级 | 操操操av | 99久久99久久免费精品蜜臀 | www.亚洲在线| 中文字幕精品一区久久久久 | 亚洲精品久久久久久国 | 成人小视频在线观看免费 | 天堂av在线网址 | 丁香六月伊人 | www久久 | 国产亚洲精品久久久久动 | 美女网站在线看 | 中文字幕888| 国产在线高清 | 国产成人精品久久亚洲高清不卡 | 九九热在线视频免费观看 | 精品久久久国产 | 日韩精品最新在线观看 | 91中文字幕一区 | 国产五码一区 | 999在线精品| a一片一级 | 在线观看91精品国产网站 | 日本在线观看视频一区 | 四虎在线免费视频 | 蜜桃视频成人在线观看 | 久久精彩免费视频 | 免费观看一级特黄欧美大片 | 中文不卡视频 | 国产精品一区专区欧美日韩 | 久久人人插 | 视频在线91 | 亚洲精品18日本一区app | 99国产精品视频免费观看一公开 | 日韩高清在线一区二区三区 | 激情综合中文娱乐网 | 网站在线观看你们懂的 | 97天天干| 欧美一区二区在线看 | 免费在线播放黄色 | 深爱开心激情 | 亚洲成年人在线播放 | 久草视频在线观 | 亚洲精品999 | 久久国产精品久久久久 | 亚洲免费专区 | 欧美在线视频第一页 | 亚洲精品短视频 | 国产在线精品观看 | 在线观看国产成人av片 | 91精品欧美一区二区三区 | 亚洲黄色av网址 | av电影一区 | 亚洲人在线7777777精品 | 天天干天天操天天射 | 国产中文在线播放 | 久久久久久久久久久免费av | 色婷婷视频在线 | 免费高清在线观看成人 | 久草在线观看资源 | 国产精品美女久久久久aⅴ 干干夜夜 | 国产精品一区在线观看你懂的 | 欧美日韩免费一区二区 | 免费观看版 | 天天爽天天爽天天爽 | 国产午夜精品一区二区三区在线观看 | 色中射 | 久久久久女教师免费一区 | 精品国产乱码久久久久久天美 | 久久99精品国产一区二区三区 | av在线专区 | 激情av网址 | 欧美日韩中文字幕视频 | 色综合天天视频在线观看 | 国产一区免费在线 | 国产精品18久久久久vr手机版特色 | 最新国产中文字幕 | 成人av直播| 日韩欧美有码在线 | 精品中文字幕在线播放 | 色多多污污在线观看 | 日本在线观看一区二区三区 | 激情欧美一区二区三区免费看 | 一本一本久久a久久精品综合 | 欧美性做爰猛烈叫床潮 | 欧美激情视频在线免费观看 | 国产成人三级在线 | 天天射天天干天天爽 | 999视频网| 黄色av网站在线观看免费 | 日日干夜夜骑 | 久久久免费高清视频 | 中文字幕在线视频网站 | 丁香五月亚洲综合在线 | 成人av资源网站 | 精品久久久久久久久久久久久 | 欧美日韩一区二区在线观看 | 超碰激情在线 | 精品国产不卡 | 欧美黑吊大战白妞欧美 | 又黄又爽又刺激 | 久久综合视频网 | 一级性生活片 | 91大神精品视频在线观看 | 免费精品视频 | 亚洲精品一区二区三区高潮 | 狠狠久久婷婷 | 久久99精品一区二区三区三区 | 5月丁香婷婷综合 | 日本久久91 | 免费看v片网站 | 一区二区三区在线视频观看58 | 青青河边草免费视频 | 日韩av免费在线电影 | 国产又黄又爽无遮挡 | 久久男人免费视频 | 五月婷婷六月丁香激情 | 亚洲少妇久久 | 亚洲天堂网在线播放 | 91最新在线观看 | 伊人中文网 | 亚洲成年人免费网站 | 99亚洲天堂 | 99视频精品免费视频 | 日韩欧美一区二区三区视频 | 亚洲一区二区三区91 | 激情视频免费观看 | 国产成人免费观看久久久 | 毛片网站观看 | 国产精品video| 亚洲精品视频中文字幕 | 国产黄色片免费看 | www.夜夜爱 | 日韩欧美在线视频一区二区 | 久草www| 精品日韩在线一区 | 国产精品99久久久久久久久久久久 | 夜夜操天天操 | 黄色网在线播放 | 999亚洲国产996395 | 亚洲国产理论片 | av片在线观看免费 | 久久久鲁 | 中文字幕亚洲精品日韩 | 美女网站久久 | 亚洲国产精品免费 | 娇妻呻吟一区二区三区 | 日本公妇色中文字幕 | 天天干夜夜操视频 | 天躁狠狠躁 | 久久精品欧美一区二区三区麻豆 | 久久视频精品 | 国产成人专区 | 日日弄天天弄美女bbbb | 日韩av网页 | 在线欧美最极品的av | 久久99免费 | 国产成人精品999在线观看 | 国产人成精品一区二区三 | 亚洲最大免费成人网 | 99精品免费久久久久久久久日本 | 天天色图 | 国产69精品久久99不卡的观看体验 | 欧美性粗大hdvideo | 激情片av | 免费高清看电视网站 | 99精品视频在线观看播放 | 黄色国产精品 | 国产亚洲激情视频在线 | 中文字幕91在线 | av久久在线 | 色吊丝在线永久观看最新版本 | 久久老司机精品视频 | 精品国产福利在线 | 狠狠狠狠狠狠狠狠干 | 亚洲永久精品在线 | 免费精品视频 | 四虎成人免费观看 | 美女国产| 在线观看一级 | 国产99色| 日韩中文字幕国产精品 | 97热久久免费频精品99 | 国产黄在线 | 色瓜 | 国产一卡在线 | 久草久草久草久草 | 日日爽视频 | 人人添人人澡人人澡人人人爽 | 婷婷中文字幕 | 亚洲欧美在线视频免费 | 免费视频一区二区 | 亚洲精品综合一区二区 | 91精品国产91久久久久 | 色在线视频| www.一区二区三区 | 国产一区二区三区免费在线观看 | 美女视频黄频大全免费 | 国产精品久久久亚洲 | 国产午夜精品理论片在线 | 亚洲激情网站免费观看 | 色婷婷激情四射 | 国产专区一 | 亚洲女人av | 天天爱综合| 天天干天天搞天天射 | 久久午夜羞羞影院 | 99久久精品久久久久久动态片 | 国产手机av | 免费成视频 | 91中文字幕在线 | 久久精品7 | 涩涩资源网 | 国产精品精品久久久 | 麻豆国产精品va在线观看不卡 | 国产一区二区网址 | 国产拍揄自揄精品视频麻豆 | 成人av播放| av播放在线 | 久久精品国产成人精品 | 亚洲成人免费观看 | 日韩中文字幕免费在线观看 | 成人av免费播放 | 国产五月色婷婷六月丁香视频 | 99久久精品免费 | 97中文字幕| 亚洲天堂网在线视频 | 色五月成人 | 99久久久久久久久 | 亚洲婷婷在线 | 欧美性超爽 | 国产在线精品国自产拍影院 | 在线观看精品一区 | 麻豆视频在线观看免费 | 美女国产网站 | 久久影院中文字幕 | 日韩视频一二三区 | 91精品国产综合久久福利 | 色综合久久88色综合天天免费 | 久久av网址 | 在线观看小视频 | 国内精品久久久久影院优 | 免费a级大片| 色欧美视频| 韩日在线一区 | 日本中文字幕在线免费观看 | 国产精久久久久久妇女av | 色姑娘综合| 探花视频在线观看 | 在线播放日韩 | 色多多视频在线观看 | 波多野结衣精品视频 | 男女精品久久 | 午夜视频在线观看一区二区三区 | 久久综合国产伦精品免费 | 国产香蕉97碰碰久久人人 | 天天综合色天天综合 | 色综合色综合久久综合频道88 | japanesefreesex中国少妇 | 天天躁日日躁狠狠躁av中文 | 久久视频这里只有精品 | 一级免费黄视频 | 97综合网 | 亚洲精品国产精品乱码在线观看 | 免费黄色在线网站 | 久久成人在线 | 色婷婷视频在线观看 | 欧美日韩亚洲国产一区 | 中文字幕激情 | 国产精品一区二区三区在线 | 免费电影一区二区三区 | 日韩精品一区不卡 | 五月婷婷综合在线 | 日本中文字幕电影在线免费观看 | 高清不卡一区二区三区 | 国产视频在线免费 | av一级在线 | 国产精品视频免费看 | 九九国产精品视频 | 天天干天天干天天干天天干天天干天天干 | a在线一区 | 97超碰在线久草超碰在线观看 | 久久伊99综合婷婷久久伊 | 国产小视频91 | 日韩欧美一区二区不卡 | 久久免费视频在线观看30 | 激情视频免费观看 | 中文字幕韩在线第一页 | 免费观看的黄色 | a√资源在线 | 亚洲精品在线观看不卡 | www91在线观看 | 欧美亚洲一级片 | 成人黄色片在线播放 | 久久免费播放 | 热re99久久精品国产66热 | 五月天婷婷狠狠 | 四虎海外影库www4hu | 婷婷免费在线视频 | 午夜精品一二三区 | 亚洲经典中文字幕 | av免费网 | 成人h动漫精品一区二 | 国产热re99久久6国产精品 | 国色天香在线观看 | 国产精品国产精品 | 久久精品亚洲精品国产欧美 | 久草在线 | 草樱av| 香蕉在线影院 | 激情综合电影网 | 奇米先锋| 久久久精品高清 | 久草免费手机视频 | 久久国产免费看 | 国产精品一区二区久久久 | 日本成人黄色片 | 久久a国产 | 综合网久久 | 99久久婷婷国产一区二区三区 | 亚洲精品久久久久久久不卡四虎 | 欧美少妇18p | 91av福利视频| 在线欧美a | 亚州av免费 | 婷婷中文字幕 | 91成人在线网站 | 天天色综合久久 | 日韩视频一二三区 | 高清不卡一区二区在线 | 精品国产一区二区三区久久久 | 日本在线成人 | 久视频在线播放 | 欧美性爽爽 | 日韩欧美一区二区在线观看 | 日韩亚洲在线视频 | 久久婷婷国产色一区二区三区 | 99精品免费 | 久草在线 | 天天操人人要 | 日本3级在线观看 | 久久久久久欧美二区电影网 | 97免费| 91视频 - 114av| 免费看一级黄色大全 | 一区国产精品 | 色av婷婷 | 国产精品视频你懂的 | 欧美午夜剧场 | 综合色影院| 一区在线观看 | 中文资源在线播放 | 久久国产精品视频免费看 | 国内久久久久久 | 国产一级二级在线 | 欧美最猛性xxxxx(亚洲精品) | 最新国产精品拍自在线播放 | 欧美一级免费在线 | 久久精品伊人 | 69成人在线| 中文字幕视频免费观看 | 久久综合9988久久爱 | 午夜三级大片 | 日韩综合一区二区三区 | 成人av影院在线观看 | 一级精品视频在线观看宜春院 | 成人久久久久久久久 | 国产精品成人久久久 | 国产麻豆果冻传媒在线观看 | 蜜桃视频日韩 | 国产在线播放一区 | 正在播放亚洲精品 | 亚洲作爱 | 日韩中文字幕免费在线观看 | 色综合久久88色综合天天人守婷 | 91干干干 | 亚洲成人精品久久 | 久久精品国产亚洲精品2020 | 久草亚洲视频 | 亚洲国产成人久久综合 | 精品久久久久久久久中文字幕 | 麻豆传媒在线视频 | 久草在线中文视频 | 日韩在线网 | 樱空桃av| 99精品乱码国产在线观看 | 久久综合五月天婷婷伊人 | 国产一级免费在线观看 | 欧美贵妇性狂欢 | 黄色a级片在线观看 | 岛国一区在线 | 国产视频2 | 亚洲精品久久久久久久蜜桃 | 欧美精品久久99 | 日日摸日日添日日躁av | 中文字幕第一页av | 久草免费电影 | 婷婷激情站 | 狠狠色丁香婷婷综合久小说久 | 国产无遮挡又黄又爽馒头漫画 | 午夜精品久久久久99热app | 日本大尺码专区mv | 国产韩国精品一区二区三区 | 久久久久国产精品免费网站 | 亚洲天天摸日日摸天天欢 | 91视视频在线直接观看在线看网页在线看 | 美女免费视频网站 | 国产成人精品综合久久久 | 免费成人av在线 | 欧美日韩在线精品 | 久久99精品国产麻豆宅宅 | 日韩毛片精品 | 国产精品高清一区二区三区 | 日韩理论片在线观看 | 奇米影视777影音先锋 | 欧美日韩国产免费视频 | 国内三级在线 | 69国产精品视频免费观看 | 中文字幕乱在线伦视频中文字幕乱码在线 | 久草在线这里只有精品 | 麻豆视频免费版 | 亚洲视频精品在线 | 亚洲高清国产视频 | 国产精品美女网站 | 色婷婷av一区 | 久久视频在线免费观看 | 久久国产高清视频 | 五月婷婷另类国产 | 国内精品视频在线播放 | 亚洲老妇xxxxxx | 欧美 另类 交 | 91丨九色丨91啦蝌蚪老版 | 免费福利片2019潦草影视午夜 | 亚洲综合色激情五月 | 97人人爽人人 | 日韩精品91偷拍在线观看 | 欧美在线一二区 | 久久久色 | 激情深爱五月 | 不卡视频在线 | 看av在线 | 狠狠躁夜夜a产精品视频 | 在线看片视频 | 精品福利在线视频 | 99国产精品一区二区 | 成人综合婷婷国产精品久久免费 | 天天操天天干天天操天天干 | 日韩精品一区电影 | 久久久国产毛片 | 在线观看麻豆av | 亚洲国产黄色 | 国产高清不卡在线 | 美女网站在线 | 黄色精品在线看 | 日本黄色a级大片 | 美女黄频在线观看 | 日韩一区二区三区观看 | 精品久久毛片 | 日韩视频在线不卡 | 五月综合久久 | 国内精品久久久久影院优 | 福利一区在线视频 | 91在线一区二区 | 久久久久久影视 | 久久婷婷一区 | 天天干天天操天天爱 | 婷婷性综合 | 国精产品永久999 | 中文乱码视频在线观看 | 国产精品国产三级国产不产一地 | 亚洲专区在线播放 | 中文字幕在线观看三区 | 欧美日韩不卡在线视频 | 国产午夜精品av一区二区 | 少妇搡bbbb搡bbb搡69 | 国产精品99蜜臀久久不卡二区 | 五月天综合色激情 | 日韩在线高清免费视频 | 天天操天天干天天爽 | 久草| 成年人天堂com | 美女视频a美女大全免费下载蜜臀 | 一级片色播影院 | 国产精品视频免费观看 | 中文字幕九九 | 中文字幕一区二区在线观看 | 国产在线自 | 成人蜜桃| 激情综合网五月 | 国产精品中文字幕在线 | 国产日韩精品在线观看 | 久久久99国产精品免费 | 99热官网 | 久久99精品国产麻豆婷婷 | 欧美日韩成人一区 | 久久国产剧场电影 | 天堂麻豆 | 欧美日韩亚洲国产一区 | 丁香网五月天 | 天天干夜夜爽 | 国产一区二区三区久久久 | 日韩中文字幕免费在线播放 | 日本黄色一级电影 | 黄色小网站在线 | 国产一级淫片免费看 | 亚洲国产精品成人精品 | a级一a一级在线观看 | 久久精品综合 | 天天操欧美 | 亚洲在线精品 | 色婷婷狠狠五月综合天色拍 | 国产精品专区在线观看 | 四虎影视精品永久在线观看 | 在线视频第一页 | 精品亚洲一区二区三区 | 超碰97在线人人 | 色婷婷av一区 | 日韩中文字幕91 | 在线观看成人网 | 亚洲狠狠婷婷 | 午夜精品一区二区三区在线观看 | 日韩在线视频看看 | 国产91精品一区二区绿帽 | 美女黄色网在线播放 | 五月婷婷视频在线 | 在线观看小视频 | 国产黄色精品在线观看 | 日韩视频一区二区在线观看 | 九九久久免费视频 | 日韩欧美有码在线 | 久久久综合精品 | 丁香在线视频 | 精品国产亚洲在线 | 国产资源精品 | 成人av电影在线播放 | 九热在线| 国产三级香港三韩国三级 | 五月婷婷精品 | 欧美日韩视频在线观看一区二区 | 特级黄色一级 | 国产免费高清视频 | 黄色电影小说 | 手机色站| 成人免费在线视频观看 | 久久午夜色播影院免费高清 | 97香蕉超级碰碰久久免费软件 | 日韩欧美国产免费播放 | 国产精品理论片在线观看 | 亚洲精品欧美精品 | 久久久人 | 天天操天天添天天吹 | 亚洲 中文 欧美 日韩vr 在线 | 欧美日韩激情视频8区 | 久精品视频免费观看2 | 五月天激情在线 | 午夜精品视频免费在线观看 | 综合色天天 | 激情五月在线视频 | 国产资源在线播放 | 波多野结衣精品视频 | 91mv.cool在线观看 | 1000部18岁以下禁看视频 | 九九热免费视频在线观看 | 成人av一区二区在线观看 | 午夜国产福利在线 | 国产成人精品综合久久久久99 | 香蕉在线影院 | 久久精品超碰 | 亚洲天堂免费视频 | 国产精品日韩在线播放 | 激情久久久久久久久久久久久久久久 | 国产黄色精品在线 | 91九色视频 | av电影在线免费观看 | 一区视频在线 | 国产成人一区二区三区在线观看 | 最新三级在线 | 日本美女xx | 奇米影视8888在线观看大全免费 | 精品国内自产拍在线观看视频 | 国产精品美女免费视频 | 日韩免费成人av | 成人天堂网 | 蜜臀av在线一区二区三区 | 99精品色 | 一区二区欧美激情 | 久久精品婷婷 | 日韩精品一区二区在线视频 | 五月网婷婷 | 国产在线精品一区二区 | 美女黄频视频大全 | 日韩| 日本黄色一级电影 | 亚洲综合一区二区精品导航 | 欧美特一级 | 99在线观看视频 | 亚州激情视频 | 国产精品视频99 | 99久久这里有精品 | 五月天亚洲综合 | 在线国产欧美 | 国产专区在线视频 | www蜜桃视频 | 中文字幕免费成人 | 亚洲无吗av| 美女av免费看| 精品免费观看视频 | 黄色毛片网站在线观看 | 在线 影视 一区 | 国产视频在线一区二区 | 午夜精品久久久久久久99 | 中文字幕日韩在线播放 | 成人a免费 | 国产福利91精品 | 99热在线免费观看 | 中文字幕在线观看日本 | 国产高清不卡一区二区三区 | 天天天天射| 成人精品一区二区三区电影免费 | 九精品| 天天激情综合网 | 天天操夜夜叫 | 开心综合网 | 四虎影视4hu4虎成人 | 成人高清在线观看 | 在线看中文字幕 | 99热手机在线 | 色婷婷99| 麻豆91视频| 男女啪啪免费网站 | 五月激情姐姐 | 69成人在线| 国产色黄网站 | 亚洲精品久久激情国产片 | 日韩久久精品 | 91成人在线视频 | 深爱婷婷网 | 天天操天天操天天操天天 | 欧美日韩午夜爽爽 | 亚洲精品一区二区三区在线观看 | 久久香蕉电影网 | 久久久综合九色合综国产精品 | 欧美国产精品久久久久久免费 | 成人资源在线观看 | 久久好看| wwwww.国产 | 国产一区二区中文字幕 | 四虎影视av | 日韩在线一二三区 | 久久精品亚洲一区二区三区观看模式 | 中文字幕在线视频网站 | 精品在线观看一区二区三区 | 国产一区精品在线 | 成人免费视频a | 婷婷丁香色综合狠狠色 | 在线观看国产www | 日韩欧美v| 亚洲精品久久久蜜桃直播 | 中文字幕在线观看一区二区 | 欧美日韩中文另类 | 狠狠干综合网 | 久草干 | 91免费看黄 | av在线免费在线观看 | 欧美日韩高清一区二区 | 最近中文字幕免费视频 | 激情中文在线 | 奇人奇案qvod | 中文字幕色站 | 欧美一区,二区 | av资源免费在线观看 | 三级av在线 | 欧美精品一区二区在线观看 | 在线成人一区二区 | 久久亚洲二区 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 欧美日韩国产区 | 国产精品久久久久久久久久久免费看 | 国产一区二区手机在线观看 | 成人av资源网站 | 国产亚洲精品久久久久秋 | 国产精品高潮呻吟久久av无 | 黄网av在线 | 天天av在线播放 | 色片网站在线观看 | 亚洲黄色免费网站 | 一区 二区 精品 | 国产精品久久久久久久久久新婚 | 久草在线免费新视频 | 四虎影视精品永久在线观看 | 五月婷婷.com | 黄色免费大片 | 久久99亚洲精品 | 亚洲精品视频在线免费播放 | 久久久黄视频 | 日韩黄色影院 | 中文字幕在线免费看 | 日韩精品观看 | 丁香久久婷婷 | 午夜免费视频网站 | 久久免费国产精品 | 久草在线这里只有精品 | 日韩69视频 | 天天色棕合合合合合合 | 中文字幕人成乱码在线观看 | 色在线视频 | 国产精品久久久视频 | 亚洲成人网av | 免费热情视频 | 性色av免费观看 | 免费看国产a | 国产视频精品免费 | 手机在线看片日韩 | 免费美女av| 欧美一区二区三区四区夜夜大片 | 成人av电影在线 | 亚洲h在线播放在线观看h | 久久久久网址 | 视频精品一区二区三区 | 热久久视久久精品18亚洲精品 | 中文字幕韩在线第一页 | 亚洲成人高清在线 | 色婷婷狠狠五月综合天色拍 | av电影av在线 | 亚洲精品在线观看不卡 | 久久久久国产一区二区 | 亚洲欧美成aⅴ人在线观看 四虎在线观看 | 久久婷婷激情 | 国产一在线精品一区在线观看 | 亚洲高清国产视频 | 国产一级久久久 | 青青久草在线视频 | 国产精品免费麻豆入口 | 久久精品理论 | 四虎影院在线观看av | 亚洲综合射 | 1000部国产精品成人观看 | 99久久精品久久久久久清纯 | 亚洲黄色在线免费观看 | 国产精品igao视频网入口 | 亚洲九九九在线观看 | 免费观看不卡av | 成人av在线网 | 亚洲欧美日本一区二区三区 | 玖玖综合网 | 欧美成年黄网站色视频 | 国产一级二级在线播放 | 国产xxxx做受性欧美88 | 91少妇精拍在线播放 | 在线观看成人 | 成人在线播放视频 | 亚洲视频www | 日韩亚洲国产精品 | 国产视频久久久久 | 色噜噜在线观看视频 | 国产污视频在线观看 | 精品久久久久一区二区国产 | 日韩三级视频在线观看 | 国产精品 日本 | 91精品国产高清自在线观看 | 国产精品一区二区电影 | 伊人资源站| 午夜精品久久久久久久久久久久久久 | 亚洲精品97| 视频一区二区在线观看 | 日韩av有码在线 | 久久都是精品 | 网站在线观看你们懂的 | 日本韩国精品一区二区在线观看 | 免费在线观看的av网站 | 久久理论影院 | 91视频三区| 久亚洲精品 | 国产999视频在线观看 | 免费成人在线网站 | 久久国产影视 |