(为了让流程更清晰,我删掉了各种错误处理与返回值判断等等,实际中还是要判断判断滴)
1、先看处理请求的handler,不是智障应该都能看懂:
static ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r) { //丢弃掉请求的body ngx_http_discard_request_body(r); ngx_str_t response = ngx_string("Hello world"); //分配内存,因为是异步的关系,所以不能发送栈区内存中的数据 //否则栈被析构了请求还没发,等框架准备好了再来发的时候,那块内存就已经不是“Hello world”了 ngx_buf_t* b; b = ngx_create_temp_buf(r->pool, response.len); ngx_memcpy(b->pos, response.data, response.len); //以下步骤是必须的,否则不会发送任何相应 b->last = b->pos + response.len; b->last_buf = 1; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = response.len; ngx_http_send_header(r); //一个ngx_buf_t的链表 ngx_chain_t out; out.buf = b; out.next = NULL; return ngx_http_output_filter(r, &out); }
那么问题来了,这tmd怎么被调用的?
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); //在模块内容被解析的时候,会有一个函数链(handlers)被挨个调用,我们把上面那玩意儿 //加到这个handlers里面去就行了。 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; }
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 */ };
看博客的你心想:“凭借哥180的智商断定这函数是在解析完配置文件被调用的!”,笔者为阁下有
如此高的领悟力深感欣慰!
此配置就是在框架初始化时的八个回调函数。
前戏太长,看博客的你可能心里不太耐烦了,话不多说让咱看看配置文件:
location /test { hello_string Hello world; }
咱的目的:访问 ip/test时输出hello world。框架如何解析hello_string配置项的?
static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello_string"), //模块名称 NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1, //location级别内,不带参数,或者带一个参数 ngx_http_hello_string, //解析配置文件的方法 NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_string), NULL }, ngx_null_command };
我们有了上下文,也有了命令,接下来就要定义模块了,让他们形成漂亮的3p:
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 };
哥尼玛日了狗了,两函数差点没看懂,搞配置文件的俩函数(差点因为这俩函数b都装不成了)
咱输出Hello world不太需要处理配置文件,后面详解配置模块:
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); //返回分配好的结构 return local_conf; } static char * ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; //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; }
好了,可以编译我们的模块了
$mkdir nginx-module $cd nginx-module $touch ngx_http_mytest_module.c config
福利,完整的.c文件代码:
#include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> typedef struct { ngx_str_t hello_string; }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 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_null_command }; 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_http_discard_request_body(r); ngx_str_t response = ngx_string("Hello world"); ngx_buf_t* b; b = ngx_create_temp_buf(r->pool, response.len); ngx_memcpy(b->pos, response.data, response.len); b->last = b->pos + response.len; b->last_buf = 1; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = response.len; ngx_http_send_header(r); ngx_chain_t out; out.buf = b; out.next = NULL; 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); return local_conf; } 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 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; }
哥复制粘贴这么累,你不点个赞对的起我?
config文件,模块的名称,源文件名:
ngx_add_name=ngx_http_hello_module HTTP_MODULES="$HTTP_MODULES ngx_http_hello_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c"
找到你nginx源码的目录(你不要傻不拉唧的真写yourpath):
$./configure --add-module=yourpath/nginx-module/ && make && make install
一般安装目录在/usr/local/nginx下面
修改配置文件:
$vim /usr/local/nginx/conf/nginx.conf location /test{ hello_string sb; }
运行nginx:
$/usr/local/nginx/sbin/nginx
浏览器访问:
另:
在gdb调试handler的时候,要去调试worker进程
$ps -aux|grep nginx $gdb ...)attach nginx_worker_pid ...)b XXX_handler ...)c //发送请求后执行c命令