咱们以一个最简单的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