咱們以一個最簡單的Hello World模塊爲例,學習Nginx的模塊編寫。假設咱們的模塊在nginx配置文件中的指令名稱爲hello_world
,那咱們就能夠在nginx.conf文件中配置這個指令nginx
location / { hello_world; }
這樣,當咱們訪問首頁的時候就會執行hello_world
指令,輸出Hello World。接下來,就開始編寫咱們的Hello World模塊,按照nginx命名規則,咱們把這個模塊取名爲ngx_http_hello_world_module
shell
咱們先建一個目錄hello_world
來保存咱們的擴展vim
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"
編寫好了config文件以後,就能夠編寫config文件中定義的模塊文件了,新建模塊文件curl
vim /opt/nginx/ext/hello_world/ngx_http_hello_world_module.c
在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_http_module_t
接口這個結構體針對本文的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
表示。咱們編寫的模塊的命令結構以下post
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 }
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; }
hello_world
配置項所屬的配置塊,而後賦值給變量clcf
hello_wrold
相匹配,那麼就調用這handler指向的方法ngx_http_hello_world_handler
前面分析,當匹配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); }
通過上面的步驟,咱們的擴展模塊下面有兩個文件在/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
修改nginx配置文件nginx.conf,若是不想配置規則,能夠直接讓訪問首頁的時候就執行hello_world執行
location / { hello_world; }
而後啓動咱們的nginx
shell> ./sbin/nginx
測試咱們的nginx擴展的HTTP模塊
shell> curl "127.0.0.1" Hello World