給你們看nginx源碼時的一個簡單引導。該文章圍繞nginx模塊展開,只是讓你們對模塊框架有個大致瞭解,並不涉及源碼分析過程。java
1, 高度模塊化的設計是nginx的架構基礎,在nginx中,除了少許的核心代碼,其餘一切皆爲模塊。nginx
高度抽象的模塊接口,全部的模塊都遵循一樣的ngx_module_t接口設計規範,他也是全部模塊的通用接口。數組
在看ngx_module_t 結構體時重點關注如下四個成員架構
*ctx指針, type成員,ctx_index成員,index成員框架
1)*ctx指針,它能夠指向任何數據,也就能夠指向任何類型的模塊。模塊化
對於HTTP類型的模塊來講,ngx_module_t中的ctx指針必須指向ngx_http_module_t(ngx_http_config.h)接口,例如:ngx_http_core_module.c、ngx_http_header_filter_module.c、ngx_http_realip_module.c等。函數
對於EVENT類型的模塊來講,ngx_module_t中的ctx指針必須指向ngx_event_module_t接口,例如:ngx_epoll_module.c、ngx_poll_module.c、ngx_select_module.c等。源碼分析
對於MAIL類型的模塊來講,ngx_module_t中的ctx指針必須指向ngx_mail_module_t接口,例如:ngx_mail_auth_http_module.c、ngx_mail_core_module.c,ngx_mail_pop3_module.c等。post
對於核心模塊,ngx_module_t中ctx指針指向ngx_core_module_t接口,例如:nginx.c(ngx_core_module)、ngx_log.c(ngx_errlog_module)、ngx_event.c (ngx_events_module)、ngx_event_openssl.c(ngx_openssl_module)、ngx_http.c(ngx_http_module)、ngx_mail.c(ngx_mail_module)這是目前官方的6大核心類型模塊。以及比較特殊的配置類型模塊ngx_conf_file.c,是惟一一個只有1個模塊的模塊類型。ui
2)type成員,定義當前模塊是那種類型的模塊。
例如:NGX_HTTP_MODULE、NGX_EVENT_MODULE、NGX_MAIL_MODULE、NGX_CORE_MODULE、NGX_CONF_MODULE,官方指定就這5個模塊
3)ctx_index成員,表示當前模塊在一類模塊中的序號。也是一個請求處理的順序。
這個成員經常是有管理這類模塊的一個核心模塊設置的。例如:HTTP模塊,ctx_index是有核心模塊ngx_http.c(ngx_http_module)設置的,有興趣同窗能夠參考147行代碼。ngx_event.c (ngx_events_module),ngx_mail.c(ngx_mail_module)這些模塊是同樣的。nginx.c(ngx_core_module)、ngx_log.c(ngx_errlog_module)、ngx_event_openssl.c(ngx_openssl_module)這三個模塊數量固定,不須要動態指定。
4)index成員,當前模塊在ngx_modules數組中的序號,也就算在全部模塊中的序號。
有興趣的同窗能夠看nginx.c中的main(int argc, char *const *argv)方法263行。
2,在nginx模塊開發中大部分都是http模塊,那麼咱們來看一下http模塊的結構體 ngx_http_module_t
HTTP框架在讀取、重載配置時定義了ngx_http_module_t,該結構體對應當前HTTP模塊啓動過程的8個階段
typedef struct { //解析配置文件前調用 ngx_int_t (*preconfiguration)(ngx_conf_t *cf); //完成配置文件的解析後調用 ngx_int_t (*postconfiguration)(ngx_conf_t *cf); //當須要建立用於存儲main級別的全局配置項時。(直屬於虛擬主機http{...}) void *(*create_main_conf)(ngx_conf_t *cf); //初始化main基本配置項 char *(*init_main_conf)(ngx_conf_t *cf, void *conf); //當須要建立用於存儲srv級別的配置項時。(直屬於虛擬主機server{...}) void *(*create_srv_conf)(ngx_conf_t *cf); //合併main級別和srv級別的配置項 char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); //當須要建立用於存儲loc級別的配置項時。(直屬於location{...}) void *(*create_loc_conf)(ngx_conf_t *cf); //合併srv級別和loc級別的配置項 char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); } ngx_http_module_t;
3,commands數組用於定義模塊的配置文件參數,每個數組元素都是ngx_command_t類型
struct ngx_command_s { //配置項名稱,也就是nginx.conf中的配置項名稱。例如:gzip upstream ngx_str_t name; //配置項類型,有三個做用:1,指明該配置項能夠出現的位置(http server location ),2,能夠攜帶參數的個數,3,參數出現的形式。用「|」分割。 ngx_uint_t type; //處理配置項的回調方法。系統預設有回調方法,能夠直接使用。 //cf: 該參數裏面保存從配置文件讀取到的原始字符串以及相關的一些信息。 //cmd: 這個配置指令對應的ngx_command_t結構。 //conf: 就是定義的存儲這個配置值的結構體 char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //配置項在內存中的相對偏移位置,由於http模塊對全部http模塊所要保存的配置信息,劃分了main, server和location三個地方進行存儲,參數應該保存到那個地方存儲,就看這個參數值。這裏可能的值爲 NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET或NGX_HTTP_LOC_CONF_OFFSET。固然也能夠直接置爲0,就是NGX_HTTP_MAIN_CONF_OFFSET。 ngx_uint_t conf; //指定該配置項值的精確存放位置,通常指定爲某一個結構體變量的字段偏移。由於對於配置信息的存儲,通常咱們都是定義個結構體來存儲的。那麼好比咱們定義了一個結構體A,該項配置的值須要存儲到該結構體的b字段。那麼在這裏就能夠填寫爲offsetof(A, b)。對於有些配置項,它的值不須要保存或者是須要保存到更爲複雜的結構中時,這裏能夠設置爲0。若是使用nginx預設的解析配置方法,就必須設在offset ngx_uint_t offset; //大多數時候,都不須要,因此簡單地設爲0便可 void *post; };
type參數字典表:
第一種做用:
NGX_DIRECT_CONF:能夠出如今配置文件中最外層。例如已經提供的配置指令daemon,master_process等。
NGX_MAIN_CONF: http、mail、events、error_log等。
NGX_ANY_CONF: 該配置指令能夠出如今任意配置級別上。
對於咱們編寫的大多數模塊而言,都是在處理http相關的事情,也就是所謂的都是NGX_HTTP_MODULE,對於這樣類型的模塊,其配置可能出現的位置也是分爲直接出如今http裏面,以及其餘位置。
NGX_HTTP_MAIN_CONF: 能夠直接出如今http配置指令裏。
NGX_HTTP_SRV_CONF: 能夠出如今http裏面的server配置指令裏。
NGX_HTTP_LOC_CONF: 能夠出如今http server塊裏面的location配置指令裏。
NGX_HTTP_UPS_CONF: 能夠出如今http裏面的upstream配置指令裏。
NGX_HTTP_SIF_CONF: 能夠出如今http裏面的server配置指令裏的if語句所在的block中。
NGX_HTTP_LMT_CONF: 能夠出如今http裏面的limit_except指令的block中。
NGX_HTTP_LIF_CONF: 能夠出如今http server塊裏面的location配置指令裏的if語句所在的block中。
第二種做用:
NGX_CONF_NOARGS:配置指令不接受任何參數。
NGX_CONF_TAKE1:配置指令接受1個參數。
NGX_CONF_TAKE2:配置指令接受2個參數。
NGX_CONF_TAKE3:配置指令接受3個參數。
NGX_CONF_TAKE4:配置指令接受4個參數。
NGX_CONF_TAKE5:配置指令接受5個參數。
NGX_CONF_TAKE6:配置指令接受6個參數。
NGX_CONF_TAKE7:配置指令接受7個參數。
能夠組合多個屬性,好比一個指令便可以不填參數,也能夠接受1個或者2個參數。那麼就是NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_TAKE2。若是寫上面三個屬性在一塊兒,你以爲麻煩,那麼沒有關係,nginx提供了一些定義,使用起來更簡潔。
NGX_CONF_TAKE12:配置指令接受1個或者2個參數。
NGX_CONF_TAKE13:配置指令接受1個或者3個參數。
NGX_CONF_TAKE23:配置指令接受2個或者3個參數。
NGX_CONF_TAKE123:配置指令接受1個或者2個或者3參數。
NGX_CONF_TAKE1234:配置指令接受1個或者2個或者3個或者4個參數。
第三種做用:
NGX_CONF_1MORE:配置指令接受至少一個參數。
NGX_CONF_2MORE:配置指令接受至少兩個參數。
NGX_CONF_MULTI: 配置指令能夠接受多個參數,即個數不定。
NGX_CONF_BLOCK:配置指令能夠接受的值是一個配置信息塊。也就是一對大括號括起來的內容。裏面能夠再包括不少的配置指令。好比常見的server指令就是這個屬性的。
NGX_CONF_FLAG:配置指令能夠接受的值是」on」或者」off」,最終會被轉成bool值。
NGX_CONF_ANY:配置指令能夠接受的任意的參數值。一個或者多個,或者」on」或者」off」,或者是配置塊。
*(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)預設的回調方法:
ngx_conf_set_flag_slot: 讀取NGX_CONF_FLAG類型的參數。
ngx_conf_set_str_slot:讀取字符串類型的參數。
ngx_conf_set_str_array_slot: 讀取字符串數組類型的參數。
ngx_conf_set_keyval_slot: 讀取鍵值對類型的參數。
ngx_conf_set_num_slot: 讀取整數類型(有符號整數ngx_int_t)的參數。
ngx_conf_set_size_slot:讀取size_t類型的參數,也就是無符號數。
ngx_conf_set_off_slot: 讀取off_t類型的參數。
ngx_conf_set_msec_slot: 讀取毫秒值類型的參數。
ngx_conf_set_sec_slot: 讀取秒值類型的參數。
ngx_conf_set_bufs_slot: 讀取的參數值是2個,一個是buf的個數,一個是buf的大小。例如: output_buffers 1 128k;
ngx_conf_set_enum_slot: 讀取枚舉類型的參數,將其轉換成整數ngx_uint_t類型。
ngx_conf_set_bitmask_slot: 讀取參數的值,並將這些參數的值以bit位的形式存儲。例如:HttpDavModule模塊的dav_methods指令。
4,HTTP請求的11個處理階段
nginx模塊設計使得每個HTTP模塊只專一本身完成的任務。而一個HTTP請求,可能就須要有無數個HTTP模塊共同合做來完成。每一個模塊何時介入處理請求,在那個模塊前,在那個模塊後。這時就須要nginx把一個HTTP的完整請求劃分出前後順序。這就有了HTTP處理的11個階段,這11個階段包括系統模塊階段,和用戶開發模塊處理階段。爲了防止系統的穩定性,nginx支持第三方模塊介入的只有7個階段。
如下定義的11個階段時有順序的,必定時按照定義的順序執行的
typedef enum { //1)HTTP框架在創建的TCP鏈接上收到完整HTTP請求頭部時。 NGX_HTTP_POST_READ_PHASE = 0, //2)Server請求地址重寫階段 NGX_HTTP_SERVER_REWRITE_PHASE, //3)配置查找階段 (該階段不容許其餘模塊介入) NGX_HTTP_FIND_CONFIG_PHASE, //4) location請求地址重寫階段 NGX_HTTP_REWRITE_PHASE, //5)請求地址重寫提交階段(該階段不容許其餘模塊介入) NGX_HTTP_POST_REWRITE_PHASE, //6)訪問權限檢查準備階段 NGX_HTTP_PREACCESS_PHASE, //7)訪問權限檢查階段 NGX_HTTP_ACCESS_PHASE, //8)訪問權限檢查提交階段(該階段不容許其餘模塊介入) NGX_HTTP_POST_ACCESS_PHASE, //9)配置項try_files處理階段(該階段不容許其餘模塊介入) NGX_HTTP_TRY_FILES_PHASE, //10) 內容產生階段 NGX_HTTP_CONTENT_PHASE, //11)日誌模塊處理階段 NGX_HTTP_LOG_PHASE } ngx_http_phases;
模塊handler的介入方式有兩種,
1)經過向全局的結構體ngx_http_core_main_conf_t的phases數組中添加handler。 例如:ngx_http_realip_module.c模塊。該方法掛載的動做通常是在模塊上下文調用的postconfiguration函數中,介入的handler在每次請求的時候都會被調用。該方法也同時使用全部階段。
2)把但願處理的handler方法設置到location相關的ngx_http_core_loc_conf_t結構體中handler指針中。每個location對應這一個獨立的ngx_http_core_loc_conf_t。例如:ngx_http_perl_module.c,ngx_http_flv_module.c等模塊。該方法掛載的動做沒必要在postconfiguration中,能夠在ngx_command_t的某個配置項的回調方法中添加處理方法。若是兩種方法同時設在回調方法,第一種方法設置的將會被覆蓋。若是一個location中有多個模塊,在前面模塊設置的handler將會被覆蓋。這種方法之只使用於NGX_HTTP_CONTENT_PHASE階段,而且只對指定的location生效。通常狀況下,咱們自定義的模塊,都使用這種方式掛載在NGX_HTTP_CONTENT_PHASE階段的。