手把手教你开发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 |
|