<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ### 關注校招、實習信息 ![](https://box.kancloud.cn/2016-09-01_57c7edd0386bb.jpg) ### Nginx 模塊概述 Nginx 模塊有三種角色: - 處理請求并產生輸出的 Handler 模塊; - 處理由?Handler?產生的輸出的 Filter(濾波器)模塊; - 當出現多個后臺服務器時,Load-balancer (負載均衡器)模塊負責選擇其中一個后臺服務器發送請求; ? ? ? 通常,服務器啟動時,任何 Handler 模塊都有可能去處理配置文件中的?location?定義。若出現多個Handler 模塊被配置成需要處理某一特定的?location?時,最終只有其中一個Handler 模塊是成功的。Handler 模塊有三種返回方式: 1. 接收請求,并成功返回; 1. 接收請求,但是出錯返回; 1. 拒絕請求,使默認的 Handler 模塊處理該請求; ? ? ? 若 Handler 模塊的作用是把一個請求反向代理到后臺服務器,則會出現另一種類型的空間模塊——?Load-balancer。?Load-balancer 負責決定將請求發送給哪個后端服務器。Nginx?目前支持兩種?Load-balancer?模塊:round-robin?(輪詢,處理請求就像打撲克時發牌那樣)和"IP hash" method(眾多請求時,保證來自同一 IP 的請求被分發的同一個后端服務器)。 ? ? ? 若 Handler 模塊沒有產生錯誤返回時,則會調用?Filter 模塊。每個location 配置里都可以添加多個Filter 模塊?,因此響應可以被壓縮和分塊。Filter 模塊之間的處理順序是在編譯時就已經確定的。Filter 模塊采用“CHAIN OF RESPONSIBILITY”鏈式的設計模式。當有請求到達時,請求依次經過這條鏈上的全部?Filter 模塊,一個Filter 被調用并處理,接下來調用下一個Filter,直到最后一個Filter 被調用完成,Nginx 才真正完成響應流程。 ? ? ? 總結如下,典型的處理形式如下: ~~~ Client sends HTTP request → Nginx chooses the appropriate handler based on the location config → (if applicable) load-balancer picks a backend server → Handler does its thing and passes each output buffer to the first filter → First filter passes the output to the second filter → second to third → third to fourth → etc. → Final response sent to client ~~~ ![](https://box.kancloud.cn/2016-09-01_57c7edd05325b.jpg) ~~~ ~~~ ### Nginx 模塊的結構 ### 模塊的配置結構 ? ? ? 模塊最多可以定義三個配置結構:main、server、location。絕大多數模塊僅需要一個location 配置。名稱約定如下以ngx_http_<module name>_(main|srv|loc)_conf_t為例的dav module: ~~~ typedef struct { ngx_uint_t methods; ngx_flag_t create_full_put_path; ngx_uint_t access; } ngx_http_dav_loc_conf_t; ~~~ ? ? ?Nginx 模塊的數據結構如下定義: ~~~ /* Nginx 模塊的數據結構 */ #define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1 #define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0 struct ngx_module_s { /* 模塊類別由type成員決定,ctx_index表示當前模塊在type類模塊中的序號 */ ngx_uint_t ctx_index; /* index 區別與ctx_index,index表示當前模塊在所有模塊中的序號 */ ngx_uint_t index; /* spare 序列保留變量,暫時不被使用 */ ngx_uint_t spare0; ngx_uint_t spare1; ngx_uint_t spare2; ngx_uint_t spare3; /* 當前模塊的版本 */ ngx_uint_t version; /* ctx指向特定類型模塊的公共接口,例如在HTTP模塊中,ctx指向ngx_http_module_t結構體 */ void *ctx; /* 處理nginx.conf中的配置項 */ ngx_command_t *commands; /* type表示當前模塊的類型 */ ngx_uint_t type; /* 下面的7個函數指針是在Nginx啟動或停止時,分別調用的7中方法 */ /* 在master進程中回調init_master */ ngx_int_t (*init_master)(ngx_log_t *log); /* 初始化所有模塊時回調init_module */ ngx_int_t (*init_module)(ngx_cycle_t *cycle); /* 在worker進程提供正常服務之前回調init_process初始化進程 */ ngx_int_t (*init_process)(ngx_cycle_t *cycle); /* 初始化多線程 */ ngx_int_t (*init_thread)(ngx_cycle_t *cycle); /* 退出多線程 */ void (*exit_thread)(ngx_cycle_t *cycle); /* 在worker進程停止服務之前回調exit_process */ void (*exit_process)(ngx_cycle_t *cycle); /* 在master進程退出之前回調exit_master */ 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; }; ~~~ ? ? ? 在該數據結構中,其中最重要的是兩個成員 ctx和commands,這里兩個成員會在分別在下面的模塊配置指令和模塊上下文中講解;若是HTTP 模塊時,type 字段必須定義為NGX_HTTP_MODULE; ### 模塊配置指令 ? ? ? 模塊指令存儲在一個 ngx_command_t 類型的靜態數組結構中,例如: ~~~ static ngx_command_t ngx_http_circle_gif_commands[] = { { ngx_string("circle_gif"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, ngx_http_circle_gif, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("circle_gif_min_radius"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_circle_gif_loc_conf_t, min_radius), NULL }, ... ngx_null_command }; ~~~ ngx_command_t?類型定義在?[core/ngx_conf_file.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h): ~~~ struct ngx_command_s { /* 配置項名稱 */ ngx_str_t name; /* 配置項類型,type將指定配置項可以出現的位置以及攜帶參數的個數 */ ngx_uint_t type; /* 處理配置項的參數 */ char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /* 在配置文件中的偏移量,conf與offset配合使用 */ ngx_uint_t conf; ngx_uint_t offset; /* 配置項讀取后的處理方法,必須指向ngx_conf_post_t 結構 */ void *post; }; ~~~ name?:配置指令的名稱; type ? ?:該配置的類型,指定配置項的出現位置以及可攜帶參數的個數,下面規定只是其中一部分,更多信息可查看文件[core/ngx_conf_file.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h): ~~~ NGX_HTTP_MAIN_CONF: directive is valid in the main config NGX_HTTP_SRV_CONF: directive is valid in the server (host) config NGX_HTTP_LOC_CONF: directive is valid in a location config NGX_HTTP_UPS_CONF: directive is valid in an upstream config NGX_CONF_NOARGS: directive can take 0 arguments NGX_CONF_TAKE1: directive can take exactly 1 argument NGX_CONF_TAKE2: directive can take exactly 2 arguments … NGX_CONF_TAKE7: directive can take exactly 7 arguments NGX_CONF_FLAG: directive takes a boolean ("on" or "off") NGX_CONF_1MORE: directive must be passed at least one argument NGX_CONF_2MORE: directive must be passed at least two arguments ~~~ set ? ??:這是一個函數指針,當Nginx 在解析配置時,若遇到該配置指令,將會把讀取到的值傳遞給這個函數進行分解處理。因為具體每個配置指令的值如何處理,只有定義這個配置指令的人是最清楚的。來看一下這個函數指針要求的函數原型。 ~~~ char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ~~~ ? ? ? ? 該函數處理成功時,返回 NGX_OK,否則返回 NGX_CONF_ERROR 或者是一個自定義的錯誤信息的字符串。該函數傳入三個類型的參數: 1. cf ? ?:指向ngx_conf_t ?結構的指針,該結構包括從配置指令傳遞的參數; 1. cmd:指向當前ngx_command_t 結構; 1. conf:指向模塊配置結構; ? ? ? 為了方便實現對配置指令參數的讀取,Nginx 已經默認提供了對一些標準類型的參數進行讀取的函數,可以直接賦值給set 字段使用。下面是一部分已經實現的set 類型函數,更多可參考文件[core/ngx_conf_file.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h): - ngx_conf_set_flag_slot?: 把 "on" 或 "off" 解析為 1 或 0; - ngx_conf_set_str_slot ? : 解析字符串并保存?ngx_str_t類型; - ngx_conf_set_num_slot: 解析一個數字并將其保存為int 類型; - ngx_conf_set_size_slot: 解析數據大小 ("8k", "1m", etc.) 并將其保存為size_t; conf ??:用于指示配置項所處內存的相對偏移量,僅在type 中沒有設置NGX_DIRECT_CONF 和NGX_MAIN_CONF 時才生效。對于HTTP 模塊,conf 必須設置,它的取值如下: - NGX_HTTP_MAIN_CONF_OFFSET:使用create_main_conf 方法產生的結構體來存儲解析出的配置項參數; - NGX_HTTP_SRV_CONF_OFFSET:使用 create_srv_conf 方法產生的結構體來存儲解析出的配置項參數; - NGX_HTTP_LOC_CONF_OFFSET:使用 create_loc_conf 方法產生的結構體來存儲解析出的配置項參數; offset?:表示當前配置項在整個存儲配置項的結構體中的偏移位置。 ### 模塊上下文 ? ? ? 這是一個靜態的 ngx_http_module_t 結構,它的名稱是ngx_http_<module name>_module_ctx。以下是該結構的定義,具體可查閱文件?[http/ngx_http_config.h](http://lxr.nginx.org/source/src/http/ngx_http_config.h): - preconfiguration - postconfiguration - creating the main conf (i.e., do a malloc and set defaults) - initializing the main conf (i.e., override the defaults with what's in nginx.conf) - creating the server conf - merging it with the main conf - creating the location conf - merging it with the server conf ~~~ typedef struct{/* 可以把不需要調用的函數指針設置為 NULL */ /* 解析配置文件之前被調用 */ 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); /* 創建存儲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; ~~~ ? ? ?在以上的結構內容中,大多數模塊只使用最后兩項:ngx_http_<module name>_create_loc_conf和ngx_http_<module name >_merge_loc_conf;例如: ~~~ static ngx_http_module_t ngx_http_circle_gif_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_circle_gif_create_loc_conf, /* create location configuration */ ngx_http_circle_gif_merge_loc_conf /* merge location configuration */ }; ~~~ ? ? ?下面針對最后兩項進行說明,以下是以 circle_gif 模塊為例子,該[模塊源碼](http://www.evanmiller.org/nginx/ngx_http_circle_gif_module.c.txt); #### create_loc_conf 函數 ? ? ?該函數是傳入一個?ngx_conf_t 結構的參數,返回新創建模塊的配置結構,在這里是返回:ngx_http_circle_gif_loc_conf_t ~~~ static void * ngx_http_circle_gif_create_loc_conf(ngx_conf_t *cf) { ngx_http_circle_gif_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_circle_gif_loc_conf_t)); if (conf == NULL) { return NGX_CONF_ERROR; } conf->min_radius = NGX_CONF_UNSET_UINT; conf->max_radius = NGX_CONF_UNSET_UINT; return conf; } ~~~ #### merge_loc_conf 函數 ? ? ? ? ?Nginx 為不同的數據類型提供了merge 函數,可查閱?[core/ngx_conf_file.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h);merge_loc_conf?函數定義如下: ~~~ static char * ngx_http_circle_gif_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_circle_gif_loc_conf_t *prev = parent; ngx_http_circle_gif_loc_conf_t *conf = child; ngx_conf_merge_uint_value(conf->min_radius, prev->min_radius, 10); ngx_conf_merge_uint_value(conf->max_radius, prev->max_radius, 20); if (conf->min_radius < 1) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "min_radius must be equal or more than 1"); return NGX_CONF_ERROR; } if (conf->max_radius < conf->min_radius) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "max_radius must be equal or more than min_radius"); return NGX_CONF_ERROR; } return NGX_CONF_OK; } ~~~ ### 模塊的定義 ? ? ? 對任何開發模塊,都需要定義一個 ngx_module_t 類型的變量來說明這個模塊本身的信息,它告訴了 Nginx 這個模塊的一些信息。這個變量是? ngx_http_<module name>_module;例如:更多例子可查找文件?[core/ngx_conf_file.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h); ~~~ ngx_module_t ngx_http_<module name>_module = { NGX_MODULE_V1, &ngx_http_<module name>_module_ctx, /* module context */ ngx_http_<module name>_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; ~~~ ### Handler 模塊 ? ? ? Handler?模塊必須提供一個真正的處理函數,這個函數負責處理來自客戶端的請求。該函數既可以選擇自己直接生成內容,也可以選擇拒絕處理,并由后續的?Handler 去進行處理,或者是選擇丟給后續的?Filter 模塊進行處理。以下是該函數的原型: ~~~ typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r); ~~~ 其中r 是?request 結構http 請求,包含客戶端請求所有的信息,例如:request method, URI, and headers。 該函數處理成功返回NGX_OK,處理發生錯誤返回NGX_ERROR,拒絕處理(留給后續的Handler 進行處理)返回NGX_DECLINE。 返回NGX_OK 也就代表給客戶端的響應已經生成,否則返回NGX_ERROR 就發生錯誤了。 ? ? ? Handler 模塊處理過程中做了四件事情:**獲取?location 配置**、**生成合適的響應**、**發送響應的?header 頭部**、**發送響應的?body 包體**。 ### 獲取?location?配置 ? ? ? 獲取?location?配置 指向調用?ngx_http_get_module_loc_conf?函數即可,該函數傳入的參數是?request 結構和 ?自定義的 module 模塊。例如:circle gif模塊; ~~~ static ngx_int_t ngx_http_circle_gif_handler(ngx_http_request_t *r) { ngx_http_circle_gif_loc_conf_t *circle_gif_config; circle_gif_config = ngx_http_get_module_loc_conf(r, ngx_http_circle_gif_module); ... } ~~~ ### 生成合適的響應 ? ? ? ?這里主要是 request 結構,其定義如下:更多可參考文件??[http/ngx_http_request.h](http://lxr.nginx.org/source/src/http/ngx_http_request.h#L316); ~~~ typedef struct { ... /* the memory pool, used in the ngx_palloc functions */ ngx_pool_t *pool; ngx_str_t uri; ngx_str_t args; ngx_http_headers_in_t headers_in; ... } ngx_http_request_t; ~~~ 其中參數的意義如下: - uri ? ? ? ? ? ?? 是?request 請求的路徑,e.g. "/query.cgi". - args ? ? ? ? ??是請求串參數中問號后面的參數(e.g. "name=john"). - headers_in 包含有用的stuff,例如:cookies 和browser 信息。 ### 發送響應的?header?頭部 ? ? ?發送響應頭部有函數ngx_http_send_header(r)?實現。響應的header?頭部在?headers_out 結構中,定義如下:更多可參考文件?[http/ngx_http_request.h](http://lxr.nginx.org/source/src/http/ngx_http_request.h#L316); ~~~ typedef stuct { ... ngx_uint_t status; size_t content_type_len; ngx_str_t content_type; ngx_table_elt_t *content_encoding; off_t content_length_n; time_t date_time; time_t last_modified_time; .. } ngx_http_headers_out_t; ~~~ ? ? ?例如,一個模塊設置為?Content-Type to "image/gif", Content-Length to 100, and return a 200 OK response,則其實現為: ~~~ r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = 100; r->headers_out.content_type.len = sizeof("image/gif") - 1; r->headers_out.content_type.data = (u_char *) "image/gif"; ngx_http_send_header(r); ~~~ ? ? ?假如content_encoding 是 (ngx_table_elt_t*)類型時,則模塊需要為這些類型分配內存,可以調用ngx_list_push 函數,實現如下: ~~~ r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers); if (r->headers_out.content_encoding == NULL) { return NGX_ERROR; } r->headers_out.content_encoding->hash = 1; r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1; r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding"; r->headers_out.content_encoding->value.len = sizeof("deflate") - 1; r->headers_out.content_encoding->value.data = (u_char *) "deflate"; ngx_http_send_header(r); ~~~ ### 發送響應的?body?包體 ? ? ?到此,該模塊已經產生響應,并把它存儲在內存中。發送包體的步驟是:首先分配響應特殊的緩沖區,然后分配緩沖區鏈接到chain link,然后在?chain link 調用發送函數。 ? ? ?1、chain links 是 Nginx ?使 Handler 模塊在緩沖區中產生響應。在 chain 中每個 chain link 有一個指向下一個 link 的指針。首先,模塊聲明緩沖區?buffer 和 chain link: ~~~ ngx_buf_t *b; ngx_chain_t out; ~~~ 2、然后分配緩沖區?buffer,使響應數據指向它: ~~~ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->pos = some_bytes; /* first position in memory of the data */ b->last = some_bytes + some_bytes_length; /* last position */ b->memory = 1; /* content is in read-only memory */ /* (i.e., filters should copy it rather than rewrite in place) */ b->last_buf = 1; /* there will be no more buffers in the request */ ~~~ 3、接著,把模塊掛載到 chain link 上: ~~~ out.buf = b; out.next = NULL; ~~~ 4、最后,發送包體: ~~~ return ngx_http_output_filter(r, &out); ~~~ ### Handler 模塊掛載 ? ? ? Handler 模塊真正的處理函數通過兩種方式掛載到處理過程中:按處理階段掛載;按需掛載。 #### 按處理階段掛載 ? ? ? 為了更精細地控制對于客戶端請求的處理過程,Nginx 把這個處理過程劃分成了11個階段。依次列舉如下: ~~~ NGX_HTTP_POST_READ_PHASE: /* 讀取請求內容階段 */ NGX_HTTP_SERVER_REWRITE_PHASE: /* Server請求地址重寫階段 */ NGX_HTTP_FIND_CONFIG_PHASE: /* 配置查找階段: */ NGX_HTTP_REWRITE_PHASE: /* Location請求地址重寫階段 */ NGX_HTTP_POST_REWRITE_PHASE: /* 請求地址重寫提交階段 */ NGX_HTTP_PREACCESS_PHASE: /* 訪問權限檢查準備階段 */ NGX_HTTP_ACCESS_PHASE: /* 訪問權限檢查階段 */ NGX_HTTP_POST_ACCESS_PHASE: /* 訪問權限檢查提交階段 */ NGX_HTTP_TRY_FILES_PHASE: /* 配置項try_files處理階段 */ NGX_HTTP_CONTENT_PHASE: /* 內容產生階段 */ NGX_HTTP_LOG_PHASE: /* 日志模塊處理階段 */ ~~~ ? ? ? 一般情況下,我們自定義的模塊,大多數是掛載在NGX_HTTP_CONTENT_PHASE階段的。掛載的動作一般是在模塊上下文調用的postconfiguration 函數中。注意:有幾個階段是特例,它不調用掛載任何的Handler,也就是你就不用掛載到這幾個階段了: ~~~ NGX_HTTP_FIND_CONFIG_PHASE NGX_HTTP_POST_ACCESS_PHASE NGX_HTTP_POST_REWRITE_PHASE NGX_HTTP_TRY_FILES_PHASE ~~~ #### 按需掛載 ? ? ? 以這種方式掛載的Handler 也被稱為content handler。當一個請求進來以后,Nginx 從NGX_HTTP_POST_READ_PHASE 階段開始依次執行每個階段中所有 Handler。執行到 ?NGX_HTTP_CONTENT_PHASE 階段時,如果這個location 有一個對應的content handler 模塊,那么就去執行這個content handler 模塊真正的處理函數。否則繼續依次執行NGX_HTTP_CONTENT_PHASE 階段中所有content phase handlers,直到某個函數處理返回NGX_OK 或者NGX_ERROR。但是使用這個方法掛載上去的handler 有一個特點是必須在NGX_HTTP_CONTENT_PHASE 階段才能被執行。如果你想自己的handler 更早的階段被執行,那就不要使用這種掛載方式。 ? ? ? 以下是例子: ~~~ circle gif ngx_command_t looks like this: { ngx_string("circle_gif"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, ngx_http_circle_gif, 0, 0, NULL } ~~~ 掛載函數: ~~~ static char * ngx_http_circle_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_circle_gif_handler; return NGX_CONF_OK; } ~~~ ### Handler 模塊編寫 Handler 模塊編寫步驟如下: 1. 編寫模塊基本結構:包括模塊的定義,模塊上下文結構,模塊的配置結構等; 1. 實現 handler 的掛載函數;根據模塊的需求選擇正確的掛載方式; 1. 編寫 handler 處理函數;模塊的功能主要通過這個函數來完成; ### Filter 模塊 ? ? ? Filter?處理由Handler 模塊產生的響應,即僅處理由服務器發往客戶端的HTTP 響應,并不處理由客戶端發往服務器的 HTTP 請求。Filter 模塊包括過濾頭部(Header Filter)和過濾包體(Body Filter ),Filter 模塊過濾頭部處理HTTP 的頭部(HTTP headers),Filter 包體處理響應內容(response content)(即HTTP 包體),這兩個階段可以對HTTP 響應頭部和內容進行修改。 ? ? ? Filter?模塊?HTTP 響應的方法如下:定義在文件?[src/http/ngx_http_core_module.h](http://lxr.nginx.org/source/src/http/ngx_http_core_module.h) ~~~ typedef ngx_int_t (*ngx_http_output_header_filter_pt) (ngx_http_request_t *r); typedef ngx_int_t (*ngx_http_output_body_filter_pt) (ngx_http_request_t *r, ngx_chain_t *chain); ~~~ 其中,參數 r?是當前的請求,chain?是待發送的 HTTP ?響應包體; ? ? ? 所有 HTTP 過濾模塊都需要實現上面的兩個方法,在 HTTP 過濾模塊組成的鏈表中,鏈表元素就是處理方法。HTTP 框架定義了鏈表入口: ~~~ extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; extern ngx_http_output_body_filter_pt ngx_http_top_body_filter; ~~~ ? ? ? 過濾模塊鏈表中通過 next 遍歷,其定義如下:? ~~~ static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; ~~~ ? ? ? 當執行發送 HTTP 頭部或 HTTP 響應包體時,HTTP 框架是從 ?ngx_http_top_header_filter 和?ngx_http_top_body_filter 開始遍歷 HTTP 頭部過濾模塊和 HTTP 包體過來模塊。其源碼實現在文件:[src/http/ngx_http_core_module.c](http://lxr.nginx.org/source/src/http/ngx_http_core_module.c) ~~~ /* 發送 HTTP 響應頭部 */ ngx_int_t ngx_http_send_header(ngx_http_request_t *r) { if (r->header_sent) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "header already sent"); return NGX_ERROR; } if (r->err_status) { r->headers_out.status = r->err_status; r->headers_out.status_line.len = 0; } return ngx_http_top_header_filter(r); } /* 發送HTTP 響應包體 */ ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_connection_t *c; c = r->connection; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http output filter \"%V?%V\"", &r->uri, &r->args); rc = ngx_http_top_body_filter(r, in); if (rc == NGX_ERROR) { /* NGX_ERROR may be returned by any filter */ c->error = 1; } return rc; } ~~~ ### Filter 模塊相關結構 ? ? ? Filter 模塊是采用鏈表形式的,其基本結構是ngx_chain_t 和?ngx_buf_t;這兩種結構定義如下: ~~~ typedef struct ngx_chain_s ngx_chain_t; struct ngx_chain_s { ngx_buf_t *buf; ngx_chain_t *next; }; struct ngx_buf_s { u_char *pos; /* 當前buffer真實內容的起始位置 */ u_char *last; /* 當前buffer真實內容的結束位置 */ off_t file_pos; /* 在文件中真實內容的起始位置 */ off_t file_last; /* 在文件中真實內容的結束位置 */ u_char *start; /* buffer內存的開始分配的位置 */ u_char *end; /* buffer內存的結束分配的位置 */ ngx_buf_tag_t tag; /* buffer屬于哪個模塊的標志 */ ngx_file_t *file; /* buffer所引用的文件 */ /* 用來引用替換過后的buffer,以便當所有buffer輸出以后, * 這個影子buffer可以被釋放。 */ ngx_buf_t *shadow; /* the buf's content could be changed */ unsigned temporary:1; /* * the buf's content is in a memory cache or in a read only memory * and must not be changed */ unsigned memory:1; /* the buf's content is mmap()ed and must not be changed */ unsigned mmap:1; unsigned recycled:1; /* 內存可以被輸出并回收 */ unsigned in_file:1; /* buffer的內容在文件中 */ /* 馬上全部輸出buffer的內容, gzip模塊里面用得比較多 */ unsigned flush:1; /* 基本上是一段輸出鏈的最后一個buffer帶的標志,標示可以輸出, * 有些零長度的buffer也可以置該標志 */ unsigned sync:1; /* 所有請求里面最后一塊buffer,包含子請求 */ unsigned last_buf:1; /* 當前請求輸出鏈的最后一塊buffer */ unsigned last_in_chain:1; /* shadow鏈里面的最后buffer,可以釋放buffer了 */ unsigned last_shadow:1; /* 是否是暫存文件 */ unsigned temp_file:1; /* 統計用,表示使用次數 */ /* STUB */ int num; }; ~~~ ### Filter 過濾頭部 header filter 包含三個基本步驟: 1. 決定是否處理響應; 1. 對響應進行處理; 1. 調用下一個?filter; ? ? ? 例如下面的"not modified" header filter:其中?headers_out 結構可參考文件?[http/ngx_http_request.h](http://lxr.nginx.org/source/src/http/ngx_http_request.h#L220); ~~~ static ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r) { time_t if_modified_since; if_modified_since = ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len); /* step 1: decide whether to operate */ if (if_modified_since != NGX_ERROR && if_modified_since == r->headers_out.last_modified_time) { /* step 2: operate on the header */ r->headers_out.status = NGX_HTTP_NOT_MODIFIED; r->headers_out.content_type.len = 0; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); } /* step 3: call the next filter */ return ngx_http_next_header_filter(r); } ~~~ ### Filter 過濾包體 ? ? ? Filter 包體只能在chain link緩沖區buffer?中操作。模塊必須決定是否修改輸入緩沖區,或分配新的緩沖區替換當前緩沖區,或是在當前緩沖區之后還是之前插入新的緩沖區。很多模塊接收多個緩沖區,導致這些模塊在不完整的chain 緩沖區中操作。Filter 包體操作如下: ~~~ static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in); ~~~ 以下是一個例子: ~~~ /* * Let's take a simple example. * Suppose we want to insert the text "<l!-- Served by Nginx -->" to the end of every request. * First, we need to figure out if the response's final buffer is included in the buffer chain we were given. * Like I said, there's not a fancy API, so we'll be rolling our own for loop: */ ngx_chain_t *chain_link; int chain_contains_last_buffer = 0; chain_link = in; for ( ; ; ) { if (chain_link->buf->last_buf) chain_contains_last_buffer = 1; if (chain_link->next == NULL) break; chain_link = chain_link->next; } /* * Now let's bail out if we don't have that last buffer: */ if (!chain_contains_last_buffer) return ngx_http_next_body_filter(r, in); /* * Super, now the last buffer is stored in chain_link. * Now we allocate a new buffer: */ ngx_buf_t *b; b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } /* * And put some data in it: */ b->pos = (u_char *) "<!-- Served by Nginx -->"; b->last = b->pos + sizeof("<!-- Served by Nginx -->") - 1; /* * And hook the buffer into a new chain link: */ ngx_chain_t *added_link; added_link = ngx_alloc_chain_link(r->pool); if (added_link == NULL) return NGX_ERROR; added_link->buf = b; added_link->next = NULL; /* * Finally, hook the new chain link to the final chain link we found before: */ chain_link->next = added_link; /* * And reset the "last_buf" variables to reflect reality: */ chain_link->buf->last_buf = 0; added_link->buf->last_buf = 1; /* * And pass along the modified chain to the next output filter: */ return ngx_http_next_body_filter(r, in); /* * The resulting function takes much more effort than what you'd do with, say, mod_perl ($response->body =~ s/$/<!-- Served by mod_perl -->/), * but the buffer chain is a very powerful construct, allowing programmers to process data incrementally so that the client gets something as soon as possible. * However, in my opinion, the buffer chain desperately needs a cleaner interface so that programmers can't leave the chain in an inconsistent state. * For now, manipulate it at your own risk. */ ~~~ ### Filter 模塊掛載 ? ? ? Filters 模塊和Handler 模塊一樣,也是掛載到post-configuration ,如下面代碼所示: ~~~ static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { NULL, /* preconfiguration */ ngx_http_chunked_filter_init, /* postconfiguration */ ... }; ~~~ ? ? ? 其中?ngx_http_chunked_filter_init 處理如下定義: ~~~ static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf) { ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_chunked_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_chunked_body_filter; return NGX_OK; } ~~~ 由于 Filter 模塊是 “CHAIN OF RESPONSIBILITY” 鏈表模式的。Handler 模塊生成響應后,Filter 模塊調用兩個函數:ngx_http_output_filter 和?ngx_http_send_header,其中ngx_http_output_filter 函數是調用全局函數?ngx_http_top_body_filter;ngx_http_send_header 函數是調用全局函數?ngx_http_top_header_filter。 ~~~ ngx_int_t ngx_http_send_header(ngx_http_request_t *r) { ... return ngx_http_top_header_filter(r); } ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_connection_t *c; c = r->connection; rc = ngx_http_top_body_filter(r, in); if (rc == NGX_ERROR) { /* NGX_ERROR may be returned by any filter */ c->error = 1; } return rc; } ~~~ Filter 模塊的執行方式如下圖所示: ![](https://box.kancloud.cn/2016-09-01_57c7edd074156.jpg) ### Filter 模塊編寫 Filter 模塊編寫步驟如下 - 編寫基本結構:模塊定義,上下文結構,基本結構; - 初始化過濾模塊:把本模塊中處理的 HTTP 頭部的?ngx_http_output_header_filter_pt 方法與處理HTTP 包體的ngx_http_output_body_filter_pt 方法插入到過濾模塊鏈表首部; - 實現處理 HTTP 響應的方法:處理 HTTP 頭部,即 ngx_http_output_header_filter_pt 方法的實現,處理HTTP 包體的方法,即ngx_http_output_body_filter_pt 方法的實現; - 編譯安裝; ### 開發 Nginx 新模塊 ? ? ? 把自己開發的模塊編譯到 Nginx 中需要編寫兩個文件: 1. "config",該文件會被?./configure 包含; 1. "ngx_http_<your module>_module.c",該文件是定義模塊的功能; ? ? ? config 文件的編寫如下: ~~~ /* * "config" for filter modules: */ ngx_addon_name=ngx_http_<your module>_module /* 模塊的名稱 */ HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_<your module>_module" /* 保存所有 HTTP 模塊*/ NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_<your module>_module.c" /* 指定新模塊的源碼路徑 */ /* * "config" for other modules: */ ngx_addon_name=ngx_http_<your module>_module HTTP_MODULES="$HTTP_MODULES ngx_http_<your module>_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_<your module>_module.c" ~~~ ? ? ? 關于?"ngx_http_<your module>_module.c" 文件的編寫,可參考上面的Handler 模塊,同時可參考Nginx 現有的模塊:[src/http/modules/](http://lxr.nginx.org/source/src/http/modules/);例如下面的“Hello World ”代碼: ~~~ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> typedef struct { ngx_str_t hello_string; ngx_int_t hello_counter; }ngx_http_hello_loc_conf_t; static ngx_int_t ngx_http_hello_init(ngx_conf_t *cf); static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello_string"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1, ngx_http_hello_string, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_string), NULL }, { ngx_string("hello_counter"), NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_http_hello_counter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_counter), NULL }, ngx_null_command }; /* static u_char ngx_hello_default_string[] = "Default String: Hello, world!"; */ static int ngx_hello_visited_times = 0; static ngx_http_module_t ngx_http_hello_module_ctx = { NULL, /* preconfiguration */ ngx_http_hello_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_hello_create_loc_conf, /* create location configuration */ NULL /* merge location configuration */ }; ngx_module_t ngx_http_hello_module = { NGX_MODULE_V1, &ngx_http_hello_module_ctx, /* module context */ ngx_http_hello_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; ngx_http_hello_loc_conf_t* my_conf; u_char ngx_hello_string[1024] = {0}; ngx_uint_t content_length = 0; ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "ngx_http_hello_handler is called!"); my_conf = ngx_http_get_module_loc_conf(r, ngx_http_hello_module); if (my_conf->hello_string.len == 0 ) { ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string is empty!"); return NGX_DECLINED; } if (my_conf->hello_counter == NGX_CONF_UNSET || my_conf->hello_counter == 0) { ngx_sprintf(ngx_hello_string, "%s", my_conf->hello_string.data); } else { ngx_sprintf(ngx_hello_string, "%s Visited Times:%d", my_conf->hello_string.data, ++ngx_hello_visited_times); } ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string:%s", ngx_hello_string); content_length = ngx_strlen(ngx_hello_string); /* we response to 'GET' and 'HEAD' requests only */ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; } /* discard request body, since we don't need it here */ rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } /* set the 'Content-type' header */ /* *r->headers_out.content_type.len = sizeof("text/html") - 1; *r->headers_out.content_type.data = (u_char *)"text/html"; */ ngx_str_set(&r->headers_out.content_type, "text/html"); /* send the header only, if the request type is http 'HEAD' */ if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = content_length; return ngx_http_send_header(r); } /* allocate a buffer for your response body */ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* attach this buffer to the buffer chain */ out.buf = b; out.next = NULL; /* adjust the pointers of the buffer */ b->pos = ngx_hello_string; b->last = ngx_hello_string + content_length; b->memory = 1; /* this buffer is in memory */ b->last_buf = 1; /* this is the last buffer in the buffer chain */ /* set the status line */ r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = content_length; /* send the headers of your response */ rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } /* send the buffer chain of your response */ return ngx_http_output_filter(r, &out); } static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf) { ngx_http_hello_loc_conf_t* local_conf = NULL; local_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t)); if (local_conf == NULL) { return NULL; } ngx_str_null(&local_conf->hello_string); local_conf->hello_counter = NGX_CONF_UNSET; return local_conf; } /* static char *ngx_http_hello_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_hello_loc_conf_t* prev = parent; ngx_http_hello_loc_conf_t* conf = child; ngx_conf_merge_str_value(conf->hello_string, prev->hello_string, ngx_hello_default_string); ngx_conf_merge_value(conf->hello_counter, prev->hello_counter, 0); return NGX_CONF_OK; }*/ static char * ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = ngx_conf_set_str_slot(cf, cmd, conf); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_string:%s", local_conf->hello_string.data); return rv; } static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = NULL; rv = ngx_conf_set_flag_slot(cf, cmd, conf); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_counter:%d", local_conf->hello_counter); return rv; } static ngx_int_t ngx_http_hello_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_hello_handler; return NGX_OK; } ~~~ ? ? ? 寫好上面的兩個文件后,在編譯 Nginx 時,步驟如下: ~~~ ./configure --add-module=path/to/your/new/module/directory make make install ~~~ 參考資料: 《[Emiller's Guide To Nginx Module Development](http://www.evanmiller.org/nginx-modules-guide.html)》 《[nginx模塊開發篇](http://tengine.taobao.org/book/module_development.html)》 《[https://github.com/simpl/ngx_devel_kit](https://github.com/simpl/ngx_devel_kit)》
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看