Nginx開發HTTP模塊入門
我們以一個最簡單的Hello World模塊為例,學習Nginx的模塊編寫。假設我們的模塊在nginx配置文件中的指令名稱為hello_world
,那我們就可以在nginx.conf文件中配置這個指令
location / {
hello_world;
}
這樣,當我們訪問首頁的時候就會執行hello_world
指令,輸出Hello World。接下來,就開始編寫我們的Hello World模塊,按照nginx命名規則,我們把這個模塊取名為ngx_http_hello_world_module
編寫config文件
我們先建一個目錄hello_world
來保存我們的擴展
mkdir /opt/nginx/ext/hello_world
要想一個辦法把我們的模塊編譯到nginx中,我們需要在執行./configure
命令的時候加入--add-module=PATH
,這里的PATH就是我們擴展目錄,所以我們要完成./configure
需要使用的config文件,在擴展目錄下,新建config文件,
vim /opt/nginx/ext/hello_world/config
內容如下
ngx_addon_name=ngx_http_hello_world_module
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_world_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_world_module.c"
CORE_LIBS="$CORE_LIBS -lpcre"
- ngx_addon_name:僅在configure執行時使用,一般設置為模塊名稱
- HTTP_MODULES:保存所有的HTTP模塊名稱,每個HTTP模塊間由空格符相連,在重新設置HTTP_MODULES變量時,不要直接覆蓋他,應該使用空格追加模塊
- NGX_ADDON_SRCS:指定擴展模塊的源代碼
編寫模塊文件
編寫好了config文件之后,就可以編寫config文件中定義的模塊文件了,新建模塊文件
vim /opt/nginx/ext/hello_world/ngx_http_hello_world_module.c
定義HTTP模塊
在nginx中,模塊的數據類型是ngx_module_t
這個類型里面的成員詳細可以參考源碼,我們直接寫出ngx_http_hello_world_module
模塊定義
ngx_module_t ngx_http_hello_world_module = {
NGX_MODULE_V1,
&ngx_http_hello_world_module_ctx, /* module context */
ngx_http_hello_world_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
};
- NGX_MODULE_V1:表示版本號,使用內置的宏定義即可
- ngx_http_hello_world_module_ctx:這個成員很重要,設置上下文結構,HTTP框架要求,它必須指向
ngx_http_module_t
接口 - ngx_http_hello_world_commands:定義模塊的配置文件參數,這個會作用在nginx.conf文件解析
- NGX_HTTP_MODULE:表示這是一個HTTP模塊
編寫模塊上下文結構
這個結構體針對本文的HTTP模塊來說,不需要調用,設置為NULL即可,但是HTTP框架要求,它必須指向ngx_http_module_t
接口,所以我們這樣定義
static ngx_http_module_t ngx_http_hello_world_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 */
};
編寫命令結構
ngx_http_hello_world_commands
數組用於定義模塊的配置文件參數,每一個元素都是ngx_command_t
類型,數組的結尾用ngx_null_command
表示。我們編寫的模塊的命令結構如下
static ngx_command_t ngx_http_hello_world_commands[] = {
{ ngx_string("hello_world"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_hello_world,
0,
0,
NULL },
ngx_null_command
}
- hello_world:表示我們的配置項名稱,和gzip一樣,設置名稱而已
- NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS:指定出現的位置,可以出現在location{}塊中
- ngx_http_hello_world:這是
ngx_command_t
中的set成員,當某塊配置中出現hello_world
時候,nginx將會啟動ngx_http_hello_world
方法,我們需要實現該函數
編寫觸發回調函數
前面編寫完命令結構,定義了出現hello_world
配置項的時候觸發ngx_http_hello_world
方法,我們要實現這個方法
static char * ngx_http_hello_world(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_hello_world_handler;
return NGX_CONF_OK;
}
- ngx_http_conf_get_module_loc_conf:這個函數會首先找到
hello_world
配置項所屬的配置塊,然后賦值給變量clcf
- clcf->handler:HTTP框架在處理用戶請求進行到NGX_HTTP_CONTENT_PHASE階段時,如果請求的主機域名,URL和
hello_wrold
相匹配,那么就調用這handler指向的方法ngx_http_hello_world_handler
編寫對http請求的具體處理方法hander
前面分析,當匹配URL之后,真正執行的其實是我們的ngx_http_hello_world_handler
函數,所以,我們要實現這個函數
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
ngx_int_t rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
ngx_str_t type = ngx_string("text/plain");
ngx_str_t response = ngx_string("Hello World");
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = response.len;
r->headers_out.content_type = type;
rc = ngx_http_send_header(r);//發送頭部
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
ngx_buf_t *b;
b = ngx_create_temp_buf(r->pool, response.len);//異步發送,要用堆內存空間
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_memcpy(b->pos, response.data, response.len);
b->last = b->pos + response.len;
b->last_buf = 1;
ngx_chain_t out;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);//向用戶發送響應包
}
這個函數的參數是ngx_http_request_t
類型參數r,它包含了很多東西(方法,URI,協議,頭部等)。
完整的例子
將上面的步驟聯系起來,可以最終合並成我們的新模塊文件ngx_http_hello_world_module.c
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_http_mytest_commands[] = {
{
ngx_string("hello_world"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
ngx_http_hello_world,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};
static ngx_http_module_t ngx_http_hello_world_module_ctx = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
ngx_module_t ngx_http_hello_world_module = {
NGX_MODULE_V1,
&ngx_http_hello_world_module_ctx,
ngx_http_hello_world_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
static char *
ngx_http_mytest(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_hello_world_handler;
return NGX_CONF_OK;
}
static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t *r)
{
ngx_int_t rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
ngx_str_t type = ngx_string("text/plain");
ngx_str_t response = ngx_string("Hello World");
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = response.len;
r->headers_out.content_type = type;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
ngx_buf_t *b;
b = ngx_create_temp_buf(r->pool, response.len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_memcpy(b->pos, response.data, response.len);
b->last = b->pos + response.len;
b->last_buf = 1;
ngx_chain_t out;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
編譯HTTP模塊
經過上面的步驟,我們的擴展模塊下面有兩個文件在/opt/nginx/ext/hello_world
目錄中
shell> ls /opt/nginx/ext/hello_world
config
ngx_http_hello_world_module.c
我們使用測試的nginx版本為nginx-1.0.10
。在安裝過程中,執行./configure
的時候需要加入我們模塊路徑
shell> ./configure --add-module=/opt/nginx/ext/hello_world/nginx_hello_world/
然后進行安裝即可
shell> make && make install
測試HTTP模塊
修改nginx配置文件nginx.conf,如果不想配置規則,可以直接讓訪問首頁的時候就執行hello_world執行
location / {
hello_world;
}
然后啟動我們的nginx
shell> ./sbin/nginx
測試我們的nginx擴展的HTTP模塊
shell> curl "127.0.0.1"
Hello World