配色: 字号:
手把手教你开发Nginx模块
2016-09-10 | 阅:  转:  |  分享 
  
手把手教你开发Nginx模块

关于Nginx模块开发的博客资料,网上很多,很多。但是,每篇博客都只提要点,无法"stepbystep"照着做,对于初次接触Nginx开发的同学,只能像只盲目的蚂蚁瞎燥急!该篇文章没有太多技术深度,只是一步一步说明白Nginx模块的开发过程。

开发环境搭建

工欲善其事,必先利其器。个人推荐EclipseCDT作为IDE,原因很简单,代码提示与补全功能很全,完胜Codeblock这类...相信与否,试过就知道。

在ubuntu下搭建开发环境:

安装GCC编译器

apt-getinstallbuild-essential
安装pcre/openssl/zlib开发库

apt-getinstalllibpcre3-dev
apt-getinstalllibssl-dev
apt-getinstalllibzip-dev
必需安装nginx核心模块依赖的pcre,openssl,zilib开发库
安装JRE/EclipseCDT

apt-getinstallopenjdk-8-jre
wgethttp://ftp.yz.yamagata-u.ac.jp/pub/eclipse//technology/epp/downloads/release/neon/R/eclipse-cpp-neon-R-linux-gtk-x86_64.tar.gz&&tzr-xzvfeclipse-cpp-neon-R-linux-gtk-x86_64.tar.gz
下载nginx源码

wgethttp://nginx.org/download/nginx-1.10.1.tar.gz&&tar-xzvfnginx-1.10.1.tar.gz
配置CDTBuildEnvironment
添加变量,值Nginxsrc下各模块路径,用冒号分隔,例如:

/root/Workspace/nginx-1.10.1/src/core:/root/Workspace/nginx-1.10.1/src/event:/root/Workspace/nginx-1.10.1/src/http:/root/Workspace/nginx-1.10.1/src/mail:/root/Workspace/nginx-1.10.1/src/stream:/root/Workspace/nginx-1.10.1/src/os/unix
添加环境变量,创建C项目时自动作为-I选项
image
image

Nginx模块编译流程

Nginx使用configure脚本分析环境,自动生成objs结果。哪么configure如何编译第三方模块?答案是--add-module指定第三方模块目录,并将目录存为$ngx_addon_dir环境变量。执行$ngx_addon_dir/config脚本,读取模块配置。在config中的环境变量分为2种:小写的本地环境变量,大写的全局环境变量。例如:

ngx_addon_name=ngx_http_mytest_module
HTTP_MODULES="$HTTP_MODULESngx_http_mytest_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS$ngx_addon_dir/ngx_http_mytest_moudle.c"
CORE_LIBS="$CORE_LIBS-lpcre"
HTTP_MODULES中的ngx_http_mytest_module就是NGX_ADDON_SRCS中源码(如果有多个,都要写上)ngx_http_mytest_module.c中定义的ngx_module_t类型的全局变量。
可见,第三方模块的入口点就是ngx_module_t类型全局变量,该变量又关联ngx_http_module_t类型static变量,与ngx_command_t类型static数组。
在ngx_http_module_t中定义上下文配置nginx.conf解析的回调方法。
在ngx_command_t中定义配置项处理的set回调方法。
Nginx的全部操作都是异步的。在上述的方法中根据需要又会使用其他handler方法。
以上可以看成Nginx第三方模块的起式。
Upstream例子源码

config

ngx_addon_name=ngx_http_mytest_module
HTTP_MODULES="$HTTP_MODULESngx_http_mytest_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS$ngx_addon_dir/ngx_http_mytest_module.c"
源代码

#include
#include
#include
#include

typedefstruct{
ngx_http_upstream_conf_tupstream;
}mytest_conf_t;

typedefstruct{
ngx_http_status_tstatus;
ngx_str_tbackendServer;
}mytest_ctx_t;

staticvoidmytest_create_loc_conf(ngx_conf_tcf);
staticcharmytest_merge_loc_conf(ngx_conf_tcf,voidparent,voidchild);
staticngx_int_tmytest_upstream_create_request(ngx_http_request_tr);
staticngx_int_tmytest_upstream_process_status_line(ngx_http_request_tr);
staticngx_int_tmytest_upstream_process_header(ngx_http_request_tr);
staticvoidmytest_upstream_finalize_request(ngx_http_request_tr,
ngx_int_trc);
staticngx_int_tmytest_handler(ngx_http_request_tr);
staticcharmytest(ngx_conf_tcf,ngx_command_tcmd,voidconf);


staticngx_http_module_tmytest_ctx={
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
mytest_create_loc_conf,
mytest_merge_loc_conf
};

staticngx_command_tmytest_commands[]={
{
ngx_string("mytest"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
mytest,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};

ngx_module_tngx_http_mytest_module={
NGX_MODULE_V1,
&mytest_ctx,
mytest_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};

staticngx_str_tmytest_upstream_hide_headers[]=
{
ngx_string("Date"),
ngx_string("Server"),
ngx_string("X-Pad"),
ngx_string("X-Accel-Expires"),
ngx_string("X-Accel-Redirect"),
ngx_string("X-Accel-Limit-Rate"),
ngx_string("X-Accel-Buffering"),
ngx_string("X-Accel-Charset"),
ngx_null_string
};

staticvoidmytest_create_loc_conf(ngx_conf_tcf){
mytest_conf_tmycf;
mycf=(mytest_conf_t)ngx_pcalloc(cf->pool,sizeof(mytest_conf_t));
if(mycf==NULL){
returnNULL;
}

mycf->upstream.connect_timeout=60000;
mycf->upstream.send_timeout=60000;
mycf->upstream.read_timeout=60000;
mycf->upstream.store_access=0600;

mycf->upstream.buffering=0;
mycf->upstream.bufs.num=8;
mycf->upstream.bufs.size=ngx_pagesize;
mycf->upstream.buffer_size=ngx_pagesize;
mycf->upstream.busy_buffers_size=2ngx_pagesize;
mycf->upstream.temp_file_write_size=2ngx_pagesize;
mycf->upstream.max_temp_file_size=102410241024;

mycf->upstream.hide_headers=NGX_CONF_UNSET_PTR;
mycf->upstream.pass_headers=NGX_CONF_UNSET_PTR;

returnmycf;
}

staticcharmytest_merge_loc_conf(ngx_conf_tcf,voidparent,voidchild){
mytest_conf_tprev=(mytest_conf_t)parent;
mytest_conf_tconf=(mytest_conf_t)child;

ngx_hash_init_thash;
hash.max_size=100;
hash.bucket_size=1024;
hash.name="proxy_headers_hash";
if(ngx_http_upstream_hide_headers_hash(cf,&conf->upstream,&prev->upstream,mytest_upstream_hide_headers,&hash)!=NGX_OK){
returnNGX_CONF_ERROR;
}
returnNGX_CONF_OK;
}

staticngx_int_tmytest_upstream_create_request(ngx_http_request_tr){
staticngx_str_tbackendQueryLine=ngx_string("GET/search?q=%VHTTP/1.1\r\nHost:www.google.com.hk\r\nConnection:close\r\n\r\n");
ngx_int_tqueryLineLen=backendQueryLine.len+r->args.len-2;

ngx_buf_tb=ngx_create_temp_buf(r->pool,queryLineLen);
if(b==NULL)returnNGX_ERROR;
b->last=b->pos+queryLineLen;

ngx_snprintf(b->pos,queryLineLen,(char)backendQueryLine.data,&r->args);

r->upstream->request_bufs=ngx_alloc_chain_link(r->pool);
if(r->upstream->request_bufs==NULL)returnNGX_ERROR;

r->upstream->request_bufs->buf=b;
r->upstream->request_bufs->next=NULL;

r->upstream->request_sent=0;
r->upstream->header_sent=0;

r->header_hash=1;

returnNGX_OK;
}

staticngx_int_tmytest_upstream_process_status_line(ngx_http_request_tr){
size_tlen;
ngx_int_trc;
ngx_http_upstream_tu;

mytest_ctx_tctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);
if(ctx==NULL)returnNGX_ERROR;

u=r->upstream;

rc=ngx_http_parse_status_line(r,&u->buffer,&ctx->status);
if(rc==NGX_AGAIN)returnrc;
if(rc==NGX_ERROR){
ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"upstreamsenttovalidHTTP/1.0header");

r->http_version=NGX_HTTP_VERSION_9;
u->state->status=NGX_HTTP_OK;

returnNGX_OK;
}

if(u->state){
u->state->status=ctx->status.code;
}

u->headers_in.status_n=ctx->status.code;
len=ctx->status.end-ctx->status.start;
u->headers_in.status_line.len=len;
u->headers_in.status_line.data=ngx_pcalloc(r->pool,len);
if(u->headers_in.status_line.data==NULL)returnNGX_ERROR;

ngx_memcpy(u->headers_in.status_line.data,ctx->status.start,len);

u->process_header=mytest_upstream_process_header;

returnmytest_upstream_process_header(r);
}

staticngx_int_tmytest_upstream_process_header(ngx_http_request_tr){
ngx_int_trc;
ngx_table_elt_th;
ngx_http_upstream_header_thh;
ngx_http_upstream_main_conf_tumcf;

umcf=ngx_http_get_module_main_conf(r,ngx_http_upstream_module);

for(;;){
rc=ngx_http_parse_header_line(r,&r->upstream->buffer,1);
if(rc==NGX_OK){
h=ngx_list_push(&r->upstream->headers_in.headers);
if(h==NULL)returnNGX_ERROR;

h->hash=r->header_hash;
h->key.len=r->header_name_end-r->header_name_start;
h->value.len=r->header_end-r->header_start;

h->key.data=ngx_pcalloc(r->pool,h->key.len+1+h->value.len+1+h->key.len);
if(h->key.data==NULL)returnNGX_ERROR;

h->value.data=h->key.data+h->key.len+1;
h->lowcase_key=h->key.data+h->key.len+1+h->value.len+1;

ngx_memcpy(h->key.data,r->header_name_start,h->key.len);
h->key.data[h->key.len]=''\0'';
ngx_memcpy(h->value.data,r->header_start,h->value.len);
h->value.data[h->value.len]=''\0'';

if(h->key.len==r->lowcase_index){
ngx_memcpy(h->lowcase_key,r->lowcase_header,h->key.len);
}else{
ngx_strlow(h->lowcase_key,h->key.data,h->key.len);
}

hh=ngx_hash_find(&umcf->headers_in_hash,h->hash,h->lowcase_key,h->key.len);
if(hh&&hh->handler(r,h,hh->offset)!=NGX_OK)returnNGX_ERROR;

continue;
}

if(rc==NGX_HTTP_PARSE_HEADER_DONE){
if(r->upstream->headers_in.server==NULL){
h=ngx_list_push(&r->upstream->headers_in.headers);
if(h==NULL)returnNGX_ERROR;
h->hash=ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(''s'',''e''),''r''),''v''),''e''),''r'');
ngx_str_set(&h->key,"Server");
ngx_str_null(&h->value);
h->lowcase_key=(u_char)"server";
}

if(r->upstream->headers_in.date==NULL){
h=ngx_list_push(&r->upstream->headers_in.headers);
if(h==NULL)returnNGX_ERROR;
h->hash=ngx_hash(ngx_hash(ngx_hash(''d'',''a''),''t''),''e'');
ngx_str_set(&h->key,"Date");
ngx_str_null(&h->value);
h->lowcase_key=(u_char)"date";
}
returnNGX_OK;
}
if(rc==NGX_AGAIN)returnNGX_AGAIN;
ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"upstreamsentinvalidheader");

returnNGX_HTTP_UPSTREAM_FT_INVALID_HEADER;
}
}

staticvoidmytest_upstream_finalize_request(ngx_http_request_tr,ngx_int_trc){
ngx_log_error(NGX_LOG_DEBUG,r->connection->log,0,"mytest_upstream_finalize_request");
}

staticcharmytest(ngx_conf_tcf,ngx_command_tcmd,voidconf){
ngx_http_core_loc_conf_tclcf;

clcf=ngx_http_conf_get_module_loc_conf(cf,ngx_http_core_module);
clcf->handler=mytest_handler;

returnNGX_CONF_OK;
}

staticngx_int_tmytest_handler(ngx_http_request_tr){
mytest_ctx_tmyctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);
if(myctx==NULL){
myctx=ngx_pcalloc(r->pool,sizeof(mytest_ctx_t));
if(myctx==NULL)returnNGX_ERROR;
ngx_http_set_ctx(r,myctx,ngx_http_mytest_module);
}

if(ngx_http_upstream_create(r)!=NGX_OK){
ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"ngx_http_upstream_create()failed");
returnNGX_ERROR;
}

mytest_conf_tmycf=(mytest_conf_t)ngx_http_get_module_loc_conf(r,ngx_http_mytest_module);
ngx_http_upstream_tu=r->upstream;
u->conf=&mycf->upstream;
u->buffering=mycf->upstream.buffering;

u->resolved=(ngx_http_upstream_resolved_t)ngx_pcalloc(r->pool,sizeof(ngx_http_upstream_resolved_t));
if(u->resolved==NULL){
ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"ngx_pcallocresolvederror.%s",strerror(errno));
returnNGX_ERROR;
}

staticstructsockaddr_inbackendSockAddr;

structhostentpHost=gethostbyname((char)"www.google.com.hk");
if(pHost==NULL){
ngx_log_error(NGX_LOG_ERR,r->connection->log,0,"gethostbynamefail.%s",strerror(errno));
returnNGX_ERROR;
}

backendSockAddr.sin_family=AF_INET;
backendSockAddr.sin_port=htons((in_port_t)80);
charpDmsIP=inet_ntoa((structin_addr)(pHost->h_addr_list[0]));
backendSockAddr.sin_addr.s_addr=inet_addr(pDmsIP);

myctx->backendServer.data=(u_char)pDmsIP;
myctx->backendServer.len=strlen(pDmsIP);

u->resolved->sockaddr=(structsockaddr)&backendSockAddr;
u->resolved->port=htons((in_port_t)80);
u->resolved->socklen=sizeof(structsockaddr_in);
u->resolved->naddrs=1;

u->create_request=mytest_upstream_create_request;
u->process_header=mytest_upstream_process_status_line;
u->finalize_request=mytest_upstream_finalize_request;

r->main->count++;

ngx_http_upstream_init(r);
returnNGX_DONE;
}
注意:《Nginx深入解析》的demo少了这句:“u->resolved->port=htons((in_port_t)80);”,否则报错“2016/09/0911:24:18[error]28352#0:1noportinupstream"",client:127.0.0.1,server:localhost,request:"GET/mytest?q=testHTTP/1.1",host:"localhost"”

编译脚本

./configure--prefix=/usr/local/nginx--add-module=/root/Workspace/nginx-modules/ngx_http_mytest_module--with-debug
sudomake
sudomakeinstall
安装后即可到/usr/local/nginx下配置nginx.conf进行测试。

Subrequest例子源码

config

ngx_addon_name=ngx_http_mytest_module
HTTP_MODULES="$HTTP_MODULESngx_http_mytest_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS$ngx_addon_dir/ngx_http_mytest_module.c"
源代码

#include
#include
#include

typedefstruct{
ngx_str_tstock[6];
}mytest_ctx_t;

staticngx_int_tmytest_subrequest_post_handler(ngx_http_request_tr,voiddata,ngx_int_trc);
staticvoidmytest_post_handler(ngx_http_request_tr);
staticngx_int_tmytest_handler(ngx_http_request_tr);
staticcharmytest(ngx_conf_tcf,ngx_command_tcmd,voidconf);

staticngx_http_module_tmytest_conf={
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
staticngx_command_tmytest_commands[]={
{
ngx_string("mytest"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
mytest,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};

ngx_module_tngx_http_mytest_module={
NGX_MODULE_V1,
&mytest_conf,
mytest_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};

staticngx_int_tmytest_subrequest_post_handler(ngx_http_request_tr,
voiddata,ngx_int_trc){
ngx_http_request_tpr=r->parent;

mytest_ctx_tmyctx=ngx_http_get_module_ctx(pr,ngx_http_mytest_module);
pr->headers_out.status=r->headers_out.status;
if(r->headers_out.status==NGX_HTTP_OK){
intflag=0;
ngx_buf_tpRecvBuf=&r->upstream->buffer;
for(;pRecvBuf->pos!=pRecvBuf->last;pRecvBuf->pos++){
if(pRecvBuf->pos=='',''||pRecvBuf->pos==''\"''){
if(flag>0){
myctx->stock[flag-1].len=pRecvBuf->pos
-myctx->stock[flag-1].data;
}
flag++;
myctx->stock[flag-1].data=pRecvBuf->pos+1;
}
if(flag>6)
break;
}
}
pr->write_event_handler=mytest_post_handler;

returnNGX_OK;

}

staticvoidmytest_post_handler(ngx_http_request_tr){
if(r->headers_out.status!=NGX_HTTP_OK){
ngx_http_finalize_request(r,r->headers_out.status);
return;
}

mytest_ctx_tmyctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);
ngx_str_twww.wang027.comoutput_format=ngx_string("stock[%V],Today
currentprice:%V,volumn:%V");
intbodylen=output_format.len+myctx->stock[0].len+myctx->stock[1].len+myctx->stock[4].len-6;
r->headers_out.content_length_n=bodylen;

ngx_buf_tb=ngx_create_temp_buf(r->pool,bodylen);
ngx_snprintf(b->pos,bodylen,(char)output_format.data,&myctx->stock[0],&myctx->stock[1],&myctx->stock[4]);
b->last=b->pos+bodylen;
b->last_buf=1;

ngx_chain_tout;
out.buf=b;
out.next=NULL;

staticngx_str_ttype=ngx_string("text/plain;charset=GBK");
r->headers_out.content_type=type;
r->headers_out.status=NGX_HTTP_OK;

r->connection->buffered|=NGX_HTTP_WRITE_BUFFERED;
ngx_int_tret=ngx_http_send_header(r);
ret=ngx_http_output_filter(r,&out);

ngx_http_finalize_request(r,ret);
}

staticngx_int_tmytest_handler(ngx_http_request_tr){
mytest_ctx_tmyctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);
if(myctx==NULL){
myctx=ngx_pcalloc(r->pool,sizeof(mytest_ctx_t));
if(myctx==NULL)returnNGX_ERROR;
ngx_http_set_ctx(r,myctx,ngx_http_mytest_module);
}

ngx_http_post_subrequest_tpsr=ngx_pcalloc(r->pool,sizeof(ngx_http_post_subrequest_t));
if(psr==NULL)returnNGX_HTTP_INTERNAL_SERVER_ERROR;

psr->handler=mytest_subrequest_post_handler;
psr->data=myctx;

ngx_str_tsub_prefix=ngx_string("/list=");
ngx_str_tsub_location;
sub_location.len=sub_prefix.len+r->args.len;
sub_location.data=ngx_pcalloc(r->pool,sub_location.len);
ngx_snprintf(sub_location.data,sub_location.len,"%V%V",&sub_prefix,&r->args);

ngx_http_request_tsr;
ngx_int_trc=ngx_http_subrequest(r,&sub_location,NULL,&sr,psr,NGX_HTTP_SUBREQUEST_IN_MEMORY);

if(rc!=NGX_OK)returnNGX_ERROR;

returnNGX_DONE;
}

staticcharmytest(ngx_conf_tcf,ngx_command_tcmd,voidconf){
ngx_http_core_loc_conf_tclcf=ngx_http_conf_get_module_loc_conf(cf,ngx_http_core_module);
clcf->handler=mytest_handler;
returnNGX_CONF_OK;
}
编译脚本

./configure--prefix=/usr/local/nginx--add-module=/root/Workspace/nginx-modules/ngx_http_mytest_module2--with-debug
sudomake
sudomakeinstall
献花(0)
+1
(本文系thedust79首藏)