開發(fā)第一個Nginx模塊
首先在
/src
下建立文件夾mymodule
-
配置
config文件
config文件實際上是shell腳本
-
開發(fā)一個HTTP模塊需要包含如下變量
#僅僅在configure使用碱呼,一般是模塊名 ngx_addon_name=ngx_mymodule #保存所有模塊名的數(shù)組证薇,不能直接賦值覆蓋攒驰,因為還有其他模塊在里面 HTTP_MODULES="$HTTP_MODULES ngx_http_mymodule_module" #新增模塊的源碼文件 NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mymodule_module.c"
在
/src/mymodule/
下創(chuàng)建源文件ngx_http_mymodule_module.c
,必須和上面配置的一樣-
測試下
#執(zhí)行configure使用--add-module=src/mymodule添加自己開發(fā)的模塊 ./configure --add-module=src/mymodule/ --without-http_rewrite_module --without-http_gzip_module
可見已經(jīng)添加了mymodule模塊憾股,由于還沒有寫代碼负芋,就不
make install
了 -
編寫自己的模塊(C語言版本)
-
大致流程
- 1,nginx讀取到配置文件時,發(fā)現(xiàn)mymodule模塊
- 2,調(diào)用ngx_http_mymodule_commands指定的ngx_http_mymodule回調(diào)函數(shù)
- 3,ngx_http_mymodule回調(diào)時設(shè)置處理HTTP的回調(diào)函數(shù)ngx_http_mymodule_handler
-
- 首先引入需要的nginx核心模塊
```
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_string.h>
#include <ngx_http_request.h>
#include <ngx_hash.h>
- 定義一個Nginx的HTTP模塊 ngx_module_t,結(jié)構(gòu)體說明如下:
struct ngx_module_s {
//表示當前模塊在這類模塊中的序號,對于所有http模塊,
//ctx_index是由核心模塊ngx_http_module設(shè)置的
//表達優(yōu)先級和模塊位置
ngx_uint_t ctx_index;
//表示當前模塊在所有模塊中的序號
ngx_uint_t index;
//保留字段,未使用
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3;
//版本,目前只有1
ngx_uint_t version;
//用于指向一類模塊的上下文結(jié)構(gòu)體,模塊上下文。指向特定類型模塊的公共接口
void *ctx;
//將處理nginx.conf中的配置項
ngx_command_t *commands;
//模塊類型,HTTP_FILTER_MODULES劝赔,CORE_MODULES,EVENT_MODULES胆敞,HTTP_MODULES着帽,HTTP_HEADERS_FILER_MODULE
//HTTP_FILTER_MODULES --> http過濾模塊
//
//CORE_MODULES --> 核心模塊
//
//EVENT_MODULES --> 事件模塊
//
//HTTP_MODULES --> HTTP模塊
//
//HTTP_HEADERS_FILER_MODULE --> HTTP頭部過濾模塊
//還可以自定義新的模塊
ngx_uint_t type;
/**
* 七個重要的模塊回調(diào)點
*/
//master進程啟動時調(diào)用杂伟,但是目前框架從不調(diào)用,因此直接設(shè)置成NULL就行
ngx_int_t (*init_master)(ngx_log_t *log);
//初始化所有模塊時被調(diào)用仍翰,master/worker模式下赫粥,在啟動worker前完成
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
//worker子進程已經(jīng)產(chǎn)生,每個worker進程初始化過程會調(diào)用所有模塊的init_process
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
//由于nginx不支持多線程模式歉备,所以init_thread在框架中沒被調(diào)用過
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
//此函數(shù)也沒被調(diào)用
void (*exit_thread)(ngx_cycle_t *cycle);
//worker進程退出前調(diào)用
void (*exit_process)(ngx_cycle_t *cycle);
//master退出前被調(diào)用
void (*exit_master)(ngx_cycle_t *cycle);
//尚未使用傅是,用NGX_MODULE_V1_PADDING填充即可
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;
};
```
- 代碼如下:
```
static ngx_int_t ngx_http_mymodule_handler(ngx_http_request_t *r);
static char *
ngx_http_mymodule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_http_mymodule_commands[] = {
{
ngx_string("mymodule"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
//set回調(diào)函數(shù)匪燕,
//char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
//當某個配置快中出現(xiàn)mymodule時蕾羊,就會回調(diào)此函數(shù)
ngx_http_mymodule,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
//空的ngx_command_t用于表示數(shù)組結(jié)束
//#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }
ngx_null_command
};
static ngx_http_module_t ngx_http_mymodule_module_ctx = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
ngx_module_t ngx_http_mymodule_module = {
NGX_MODULE_V1,
//ctx,對于HTTP模塊來說,ctx必須是ngx_http_module_t接口
&ngx_http_mymodule_module_ctx,
//commands,
ngx_http_mymodule_commands,
//定義http模塊時帽驯,必須設(shè)置成NGX_HTTP_MODULE
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
//配置項對應(yīng)的回調(diào)函數(shù)
static char *
ngx_http_mymodule(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);
//在NGX_HTTP_CONTENT_PHASE階段會調(diào)用此回調(diào)函數(shù)
clcf->handler = ngx_http_mymodule_handler;
return NGX_CONF_OK;
}
//實際完成處理的回調(diào)函數(shù)
/*
* r 是nginx已經(jīng)處理完了的http請求頭
*/
static ngx_int_t ngx_http_mymodule_handler(ngx_http_request_t *r)
{
return NGX_HTTP_NOT_ALLOWED;
}
```
- 測試下
```
./configure --add-module=src/mymodule/ --without-http_rewrite_module --without-http_gzip_module
make install
```
配置nginx.conf
location /test{
mymodule;
}
由于直接返回了NGX_HTTP_NOT_ALLOWED(405狀態(tài)碼)
![405測試](http://upload-images.jianshu.io/upload_images/9669276-8f69f37c14c1a9ec?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-
實現(xiàn)對http請求的處理
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) { //非法請求方式 狀態(tài)碼 405 return NGX_HTTP_NOT_ALLOWED; } //丟棄客戶端發(fā)送來的HTTP包體內(nèi)容 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("codelover"); r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = response.len; r->headers_out.content_type = type; //自定義響應(yīng)頭 ngx_table_elt_t* p = ngx_list_push(&r->headers_out.headers); p->hash = 1; p->key.len = sizeof("codelover")-1; p->key.data = (u_char*)"codelover"; p->value.len = sizeof("codelover")-1; p->value.data = (u_char*)"codelover"; //發(fā)送響應(yīng)頭 rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } ngx_buf_t *b; //r->pool內(nèi)存池 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); //必須設(shè)置好last指針龟再,如果last和pos相等,是不會發(fā)送的 b->last = b->pos + response.len; //聲明這是最后一塊緩沖區(qū) b->last_buf = 1; ngx_chain_t out; out.buf = b; out.next = NULL; return ngx_http_output_filter(r, &out); ![mymodule](http://upload-images.jianshu.io/upload_images/9669276-0e1ef3e227987a81?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
至此尼变,一個簡單的nginx模塊就開發(fā)完成利凑。
-
修改成c++版本
-
1,編譯方式修改
最好不要修改configure文件,所以修改Makefile文件:
#新增g++編譯器嫌术,前提是已經(jīng)在系統(tǒng)安裝g++ CXX = g++ #修改連接器為g++ LINK = $(CXX) #修改自己的模塊的編譯方式為g++ objs/addon/mymodulecpp/ngx_http_mymodulecpp_module.o: $(ADDON_DEPS) \ src/mymodulecpp//ngx_http_mymodulecpp_module.cpp $(CXX) -c $(CFLAGS) $(ALL_INCS) \ -o objs/addon/mymodulecpp/ngx_http_mymodulecpp_module.o \ src/mymodulecpp//ngx_http_mymodulecpp_module.cpp
-
2,源碼修改
把關(guān)于nginx的頭文件使用extern "C" 括起來哀澈,保證編譯時使用的是gcc,部分回調(diào)函數(shù)也要括起來extern "C"{ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> #include <ngx_string.h> #include <ngx_http_request.h> #include <ngx_hash.h> #include <ngx_http_config.h> static ngx_int_t ngx_http_mymodulecpp_handler(ngx_http_request_t *r); static char * ngx_http_mymodulecpp(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); }
-
3,可能的錯誤
這是由于g++不支持將void* 和其他類型的指針進行隱式轉(zhuǎn)換度气,因此需要修改
clcf = (ngx_http_core_loc_conf_t *)ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
和ngx_table_elt_t* p = (ngx_table_elt_t*)ngx_list_push(&r->headers_out.headers);
進行強制轉(zhuǎn)換 -
4,參考代碼
extern "C"{ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> #include <ngx_string.h> #include <ngx_http_request.h> #include <ngx_hash.h> #include <ngx_http_config.h> static ngx_int_t ngx_http_mymodulecpp_handler(ngx_http_request_t *r); static char * ngx_http_mymodulecpp(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); } //只是包含了c++的庫 //#include <iostream> //#include <string> //using namespace std; #include "ngx_http_mymodulecpp_module.h" static ngx_command_t ngx_http_mymodulecpp_commands[] = { { ngx_string("mymodulecpp"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS, //set回調(diào)函數(shù)割按, //char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //當某個配置快中出現(xiàn)mymodulecpp時,就會回調(diào)此函數(shù) ngx_http_mymodulecpp, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, //空的ngx_command_t用于表示數(shù)組結(jié)束 //#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL } ngx_null_command }; static ngx_http_module_t ngx_http_mymodulecpp_module_ctx = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; ngx_module_t ngx_http_mymodulecpp_module = { NGX_MODULE_V1, //ctx,對于HTTP模塊來說磷籍,ctx必須是ngx_http_module_t接口 &ngx_http_mymodulecpp_module_ctx, //commands, ngx_http_mymodulecpp_commands, //定義http模塊時适荣,必須設(shè)置成NGX_HTTP_MODULE NGX_HTTP_MODULE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING }; //配置項對應(yīng)的回調(diào)函數(shù) static char * ngx_http_mymodulecpp(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf; clcf = (ngx_http_core_loc_conf_t *)ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); //在NGX_HTTP_CONTENT_PHASE階段會調(diào)用此回調(diào)函數(shù) clcf->handler = ngx_http_mymodulecpp_handler; return NGX_CONF_OK; } //實際完成處理的回調(diào)函數(shù) /* * r 是nginx已經(jīng)處理完了的http請求頭 */ static ngx_int_t ngx_http_mymodulecpp_handler(ngx_http_request_t *r) { if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) { //非法請求方式 狀態(tài)碼 405 return NGX_HTTP_NOT_ALLOWED; } //丟棄客戶端發(fā)送來的HTTP包體內(nèi)容 ngx_int_t rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } //測試c++語法是否支持 string* a = new string("cpp"); ngx_str_t type = ngx_string("text/plain"); ngx_str_t response = ngx_string(a->c_str()); r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = a->length(); r->headers_out.content_type = type; //自定義響應(yīng)頭 ngx_table_elt_t* p = (ngx_table_elt_t*)ngx_list_push(&r->headers_out.headers); p->hash = 1; p->key.len = sizeof("codelover")-1; p->key.data = (u_char*)"codelover"; p->value.len = sizeof("codelover")-1; p->value.data = (u_char*)"codelover"; //發(fā)送響應(yīng)頭 rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } ngx_buf_t *b; //r->pool內(nèi)存池 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); //必須設(shè)置好last指針,如果last和pos相等院领,是不會發(fā)送的 b->last = b->pos + response.len; //聲明這是最后一塊緩沖區(qū) b->last_buf = 1; ngx_chain_t out; out.buf = b; out.next = NULL; return ngx_http_output_filter(r, &out); }
至此就完成了使用c++進行模塊開發(fā)的例子
github 代碼 -