<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之旅 廣告
                # 7 自定義Nginx模塊 ##7.1 ngx_command_t 數組 commands 數組用于定義模塊的配置文件參數,每一個數組元素都是`ngx_command_t`類型,數組的結尾是用`ngx_numm_command`表示。Nginx在解析配置文件中的一個配置項時首先會遍歷所有的模塊,對于每一個模塊而言,即通過遍歷commands數組進行,另外,在數組中檢查到ngx_numm_command時,會停止使用當前模塊解析該配置項。每一個ngx_command_t結構體定義個如下配置: ```cpp typedef struct ngx_command_s ngx_command_t; struct ngx_command_s { //配置項名稱,如"gzip" ngx_str_t name; //配置項類型,type將制定配置項可以出現的位置, //例如:出現在server{}活location{}中,等等 ngx_uint_t type; //出現了name中指定的配置項后,將會調用set方法處理配置項的參數 char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //在配置文件中的偏移量 ngx_uint_t conf; //通常用于使用預設的解析方法解析配置項,這是配置模塊的一個優秀的設計, //需要與conf配合使用 ngx_uint_t offset; //配置項讀取后的處理方法,必須是ngx_conf_post_t結構的指針 void *post; }; ``` ngx_null_command只是一個空的ngx_command_s: ```cpp #define ngx_null_command {ngx_null_string, 0, NULL, 0, 0, NULL} ``` 也就是說,對于我們在nginx.cong 中編寫的配置項mytest來說,nginx首先會遍歷所有的模塊(modules),而對于每個模塊,會遍歷他所對應的ngx_command_t數組,試圖找到關于我們配置項目mytest的解析方式。 ```cpp static ngx_command_t ngx_http_mytest_commands[] = { { ngx_string("mytest"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS, ngx_http_mytest, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ngx_null_command }; ``` ##7.2 command中用于處理配置項參數的set方法 ```cpp static char * ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf; //首先找到mytest配置項所屬的配置塊,clcf貌似是location塊內的數據 //結構,其實不然,它可以是main、srv或者loc級別配置項,也就是說在每個 //http{}和server{}內也都有一個ngx_http_core_loc_conf_t結構體 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); //http框架在處理用戶請求進行到NGX_HTTP_CONTENT_PHASE階段時,如果 //請求的主機域名、URI與mytest配置項所在的配置塊相匹配,就將調用我們 //實現的ngx_http_mytest_handler方法處理這個請求 clcf->handler = ngx_http_mytest_handler; return NGX_CONF_OK; } ``` 關于ngx_http_conf_get_module_loc_conf 的定義可以參考: http://lxr.nginx.org/source/src/http/ngx_http_config.h#0065 >本質: 就是設置`ngx_http_mytest_handler`, 匹配項被選中的時候, 應該如何解析。 ##7.3 定義ngx_http_module_t接口 這部分的代碼, 是用于http框架的, 相當于http框架的回掉函數, 由于這里并不需要框架做任何操作。 ```cpp static ngx_http_module_t ngx_http_mytest_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL /* merge location configuration */ }; ``` ##7.4 定義處理“mytest”command的handler 我們這里需要定義配置項被匹配之后的處理方法. ```cpp static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) { //必須是GET或者HEAD方法,否則返回405 Not Allowed if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; } //丟棄請求中的包體 ngx_int_t rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } //設置返回的Content-Type。注意,ngx_str_t有一個很方便的初始化宏 //ngx_string,它可以把ngx_str_t的data和len成員都設置好 ngx_str_t type = ngx_string("text/plain"); //返回的包體內容 ngx_str_t response = ngx_string("Hello World!"); //設置返回狀態碼 r->headers_out.status = NGX_HTTP_OK; //響應包是有包體內容的,所以需要設置Content-Length長度 r->headers_out.content_length_n = response.len; //設置Content-Type r->headers_out.content_type = type; //發送http頭部 rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } //構造ngx_buf_t結構準備發送包體 ngx_buf_t *b; b = ngx_create_temp_buf(r->pool, response.len); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //將Hello World拷貝到ngx_buf_t指向的內存中 ngx_memcpy(b->pos, response.data, response.len); //注意,一定要設置好last指針 b->last = b->pos + response.len; //聲明這是最后一塊緩沖區 b->last_buf = 1; //構造發送時的ngx_chain_t結構體 ngx_chain_t out; //賦值ngx_buf_t out.buf = b; //設置next為NULL out.next = NULL; //最后一步發送包體,http框架會調ngx_http_finalize_request方法 //結束請求 return ngx_http_output_filter(r, &out); } ``` ##7.5 定義ngx_module_t中的mytest模塊 定義HTTP模塊方式很簡單,例如: ```cpp ngx_module_t ngx_http_mytest_module; ``` 其中ngx_module_t是一個Nginx模塊的數據結構,下面來分析一下Nginx模塊中的所有成員: ```cpp typedef struct ngx_module_s ngx_module_t; struct ngx_module_s { /* 下面的ctx_index、index、spare0,spare1, spare2,spare3,version變量不需要再定義時候賦值, 可以用Nginx準備好的宏NGX_MODULE_V1來定義, 已經定義好了這7個值 #define NGX_MODULE_V1 0,0,0,0,0,0,1 對于一類模塊(由下面的type成員決定類別)而言, ctx_index表示當前模塊在這類模塊中的序號。 這個成員常常是由管理這類模塊的一個Nginx核心模塊設置的, 對于所有的HTTP模塊而言,ctx_index是由 核心模塊ngx_http_module設置的。 ctx_index非常重要,Nginx的模塊化設計非常依賴于各個模塊的順序, 他們既用于表達優先級,也用于表明每個模塊的位置, 借以幫助Nginx框架快速獲得某個模塊的數據. */ ngx_uint_t ctx_index; /* index表示當前模塊在ngx_module數組中的序號。 注意,ctx_index表示的是當前模塊在一類模塊中的序號, 而index表示當前模塊在所有模塊中的序號,同樣很關鍵. Nginx啟動時會根據ngx_modules數組設置各模塊的index值, 例如: ngx_max_module = 0; for(i = 0; ngx_module[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; //模塊的版本,便于將來的拓展,目前只有一種,默認為1 ngx_uint_t version; /* ctx用于指向一類模塊的上下文結構體, 為什么需要ctx呢?因為前面說過, Nginx模塊有許多種類,不同類模塊之間的功能差別很大。 例如, 事件類型的模塊主要處理I/O事件的相關功能, HTTP類型的模塊主要處理HTTP應用層的功能。 這樣每個模塊都有了自己的特性, 而ctx會指向特定類型模塊的公共接口。 例如, 在HTTP模塊中,ctx需要指向ngx_http_module_t結構體 */ void *ctx; /* commands處理nginx.conf中的配置項 */ ngx_command_t *commands; /* type表示該模塊的類型,它與ctx指針是緊密相關的。 在官方Nginx中,它的取值范圍有以下5種: NGX_HTTP_MODULE NGX_CORE_MODULE NGX_CONF_MODULE NGX_EVENT_MODULE NGX_MAIL_MODULE */ ngx_uint_t type; /* 在Nginx的啟動、停止過程中,以下7個函數指針表示7個 執行點分別用調用這7種方法。對于任意一個方法而言, 如果不需要Nginx再某個時刻執行它,那么簡單地把它設為NULL 空指針即可。 */ /* 雖然從字面上理解應當在master進程啟動的時,回調 init_master,但到目前為止,框架代碼從來不會調動它, 所以設置為NULL */ ngx_int_t (*init_master)(ngx_log_t *log); /* init_module回調方法在初始化所有模塊時候被調用。 在master/worker模式下,這個階段將在啟動worker子進程前完成。 */ ngx_int_t (*init_module)(ngx_cycle_t *cycle); /* init_process 回調方法在正常服務前被調用。 在master/worker模式下,多個worker子進程已經產生。 在每個worker進程的初始化過程會調用所有模塊的init_process函數 */ ngx_int_t (*init_process)(ngx_cycle_t *cycle); /* 由于Nginx暫時不支持多線程模式,所以init_thread在框架中 沒有被調用過,設置為NULL */ ngx_int_t (*init_thread)(ngx_cycle_t *cycle); /* 同上、exit_thread也不支持,設為NULL */ void (*exit_thread)(ngx_cycle_t *cycle); /* exit_process回調方法在服務停止前被調用。 在/master/worker模式下,worker進程會在退出前調用 */ void (*exit_process)(ngx_cycle_t *cycle); /* exit_master回調方法將在master進程退出前被調用 */ void (*exit_master)(ngx_cycle_t *cycle); /* 一下8個spare_hook變量也是保留字段,目前沒有使用, 但可用Nginx提供的NGX_MODULE_V1_PADDING宏來填充 #define NGX_MODULE_V1_PADDING 0,0,0,0,0,0,0,0 */ 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; }; ``` 所以在定義一個HTTP模塊的時候,務必把type字段設置為NGX_HTTP_MODULE. 對于下列回調方法:init_module、init_process、exit_process、exit_master 調用他們的是Nginx框架代碼。換句話說,這4個回調方法與HTTP框架無關。 即使nginx.conf中沒有設置http{...}這種開啟HTTP功能的配置項,這些 回調方法仍然會被調用。因此,通常開發HTTP模塊時候都把他們設置為NULL。 這樣Nginx不作為web服務器使用時,不會執行HTTP模塊的任何代碼。 定義HTTP時候,最重要的是要設置ctx和commands這兩個成員。 對于HTTP類型模塊來說,ngx_module_中的ctx指針必須指向ngx_http_module_t接口。 所以最終我們定義個ngx_module_t 模塊如下: ```cpp ngx_module_t ngx_http_mytest_module = { NGX_MODULE_V1, &ngx_http_mytest_module_ctx, /* module context */ ngx_http_mytest_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 }; ``` 這樣, mytest 模塊在編譯的時候, 就可以被加入到ngx_modules的全局數組中了。 ## 7.6 完整代碼 ngx_http_mytest_module.c 所以全部的編碼工作完畢,最終的代碼應該如下: ```cpp #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> #include <sys/types.h> #include <unistd.h> //定義處理用戶請求hello world handler static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) { //必須是GET或者HEAD方法,否則返回405 Not Allowed if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; } //丟棄請求中的包體 ngx_int_t rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } //設置返回的Content-Type。注意,ngx_str_t有一個很方便的初始化宏 //ngx_string,它可以把ngx_str_t的data和len成員都設置好 ngx_str_t type = ngx_string("text/plain"); //返回的包體內容 ngx_str_t response = ngx_string("Hello World!"); //設置返回狀態碼 r->headers_out.status = NGX_HTTP_OK; //響應包是有包體內容的,所以需要設置Content-Length長度 r->headers_out.content_length_n = response.len; //設置Content-Type r->headers_out.content_type = type; //發送http頭部 rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } //構造ngx_buf_t結構準備發送包體 ngx_buf_t *b; b = ngx_create_temp_buf(r->pool, response.len); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //將Hello World拷貝到ngx_buf_t指向的內存中 ngx_memcpy(b->pos, response.data, response.len); //注意,一定要設置好last指針 b->last = b->pos + response.len; //聲明這是最后一塊緩沖區 b->last_buf = 1; //構造發送時的ngx_chain_t結構體 ngx_chain_t out; //賦值ngx_buf_t out.buf = b; //設置next為NULL out.next = NULL; //最后一步發送包體,http框架會調用ngx_http_finalize_request方法 //結束請求 return ngx_http_output_filter(r, &out); } //定義command用于處理配置項參數的set方法 static char * ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf; //首先找到mytest配置項所屬的配置塊,clcf貌似是location塊內的數據 //結構,其實不然,它可以是main、srv或者loc級別配置項,也就是說在每個 //http{}和server{}內也都有一個ngx_http_core_loc_conf_t結構體 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); //http框架在處理用戶請求進行到NGX_HTTP_CONTENT_PHASE階段時,如果 //請求的主機域名、URI與mytest配置項所在的配置塊相匹配,就將調用我們 //實現的ngx_http_mytest_handler方法處理這個請求 clcf->handler = ngx_http_mytest_handler; return NGX_CONF_OK; } //定義ngx mytest 配置匹配的command static ngx_command_t ngx_http_mytest_commands[] = { { ngx_string("mytest"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS, ngx_http_mytest, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ngx_null_command }; //定義ngx_http_module_t接口 static ngx_http_module_t ngx_http_mytest_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL /* merge location configuration */ }; //定義mytest模塊 ngx_module_t ngx_http_mytest_module = { NGX_MODULE_V1, &ngx_http_mytest_module_ctx, /* module context */ ngx_http_mytest_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 }; ``` ## 7.7 配置文件 config 但是之后我們還需要給該模塊相同目錄下提供一個配置文件"config"表示在nginx編譯的時候的一些編譯選項。 ```bash ngx_addon_name=ngx_http_mytest_module HTTP_MODULES="$HTTP_MODULES ngx_http_mytest_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c" ``` 所以我們的模塊編寫完畢了,當前目錄應該有兩個文件 ```bash ls config ngx_http_mytest_module.c ``` ## 7.8 重新編譯Nginx 并添加自定義模塊 進入Nginx源碼目錄 執行 ```bash ./configure --prefix=/usr/local/nginx --add-module=/home/ace/openSource_test/nginx_module_http_test ``` --add-module為剛才自定義模塊源碼的目錄 ```bash make sudo make install ``` ## 測試自定義模塊 修改nginx.conf文件 ```php server { listen 8777; server_name localhost; location / { mytest;#我們自定義模塊的名稱 } } ``` 重新啟動nginx 打開瀏覽器輸入地址和端口 ![](https://img.kancloud.cn/88/ae/88aec56dd54a781ee152d6c07b2c1a99_692x240.png)
                  <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>

                              哎呀哎呀视频在线观看