Nginx提供的upstream机制,是nginx设计理念的忠实体现。异步、无阻塞,这是nginx的追求,任何对这种设计思想的违反,都会导致nginx达不到它预期的性能,包括nginx提供的fastCGI也是如此。
Upstream到底用来干什么呢?就是nginx在正常的请求处理过程中,需要访问其他SERVER,这时,nginx提供了这样的机制,把底层的http通讯全部做完。最重要的是,upstream保证了在这个请求中对其他SERVER的通讯,完全是无阻塞和异步的。个人认为,如果nginx没有提供upstream,当开发者遇到这种情形要么自己写一套多路复用IO处理机制来做,要么放弃异步去调用connection里的同步方法,就不能称为真正的高性能异步WEB
SERVER了。
Upstream实现得其实非常狭隘,因为nginx试图把upstream做成一种proxy,也就是说nginx会对其他SERVER访问后,对client的返回也接管,这个很恶心。如果你不需要这个功能,需要在ngx_http_upstream_process_header里,调用你的module处理response后就返回,不要继续向下执行。
Upstream有六个需要module developer实现的方法,分别是:
ngx_int_t(*create_request)(ngx_http_request_t *r);
ngx_int_t(*reinit_request)(ngx_http_request_t *r);
ngx_int_t(*process_header)(ngx_http_request_t *r);
void(*abort_request)(ngx_http_request_t *r);
void(*finalize_request)(ngx_http_request_t *r,
ngx_int_t rc);
ngx_int_t(*rewrite_redirect)(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
upstream在真正实现HTTP通讯时会调用到这些函数。那么upstream的是如何进行的?首先,我们需要调用ngx_http_upstream_init来开始upstream之旅了。
ngx_http_upstream_init首先会去调用create_request函数,这时开发者可以在这里把HTTP请求构造好。构造完请求包后,nginx会去连接remote
server,这又是一个异步事件,所以需要注册回调函数为ngx_http_upstream_handler,也就是说,当连接成功建立后,nginx的epoll会调用ngx_http_upstream_handler来处理建立好的这个连接,同时把发送HTTP请求的处理函数注册为ngx_http_upstream_send_request_handler。
现在与remote server建立好连接了,ngx_http_upstream_handle调用ngx_http_upstream_send_request_handler来发送create_request完成的HTTP请求包了。这里实际完成发送请求任务的是ngx_http_upstream_send_request方法,而ngx_http_upstream_send_request又是调用ngx_output_chain来把请求发送出去。成功以后,开始等待读事件的来临,如果有数据返回,则调用ngx_http_upstream_process_header来处理remote
server的response了,等到确认接收到完整的response后,会调用我们实现的process_header注册函数来处理response。upstream处理完后会调用注册的finalize_request函数来清理开发者需要做的工作。
Nginx给用户提供了内存池功能,所以developer在使用时,应当尽量避免绕过nginx内存池来操作内存。
这里不去分析nginx内存池的实现,只简要的说明如何使用它。
申请一块内存时,必须先拿到内存池的指针,然后传入内存块大小,nginx实际上会移动指针指向内存池中的空闲内存,如果失败则返回NULL,内存池大小有限且可配,所以我们必须每次申请内存都要检查是否申请成功。
我们看下最简单的一个分配buf函数:
ngx_buf_t *
ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
{
ngx_buf_t*b;
b =ngx_calloc_buf(pool);
if (b ==NULL) {
returnNULL;
}
b->start= ngx_palloc(pool, size);
if(b->start == NULL) {
returnNULL;
}
b->pos =b->start;
b->last= b->start;
b->end =b->last + size;
b->temporary = 1;
return b;
}
可以看到,很简单的从内存池中申请到,释放则由pool自动进行,很好的垃圾回收机制。
在ngx_command_t中,定义好需要读取的配置项名称,以及处理该配置项的方法,这样就可以nginx.conf里放置相应的配置项,在module里使用。
例如:
在nginx.conf里加入下行配置:
dmsargname dcname filesize blobid;
则需要在module里的ngx_command_t数组里分配如下:
{
ngx_string("dmsargname"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1234,
ngx_read_dmsargname,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_webex_DMD_loc_conf_t,argnameFromDC),
NULL
},
实际的处理函数为ngx_read_dmsargname。NGX_CONF_TAKE1234意为最多读取四个参数。
读取参数函数简易实现:
static char * /* {{{ ngx_read_datastore */
ngx_read_dmsargname(ngx_conf_t *cf, ngx_command_t*cmd, void *conf)
{
ngx_http_core_loc_conf_t
*clcf;
ngx_webex_DMD_loc_conf_t
*ulcf = conf;
ngx_str_t
*value;
value =cf->args->elts;
clcf =ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
ulcf->argnameFromDC= value[1];
ulcf->argnameFilesize= value[2];
ulcf->argnameBlobID= value[3];
returnNGX_CONF_OK;
}
这个必须要说下,因为处理http请求时,必须处理head,无论是读取head还是插入head,都需要和ngx_list_t这个数据结构打交道,nginx框架是把head都放到ngx_list_theaders;数据结构里的。
list一共有三种操作:
ngx_list_create();//创建及初始化队列
ngx_list_init();//初始化队列
ngx_list_push();//找到下一个插入位置的指针并返回
很明显,设计者为了灵活性,没有提供遍历方法,那么首先,应该怎么遍历它?比如,拿到一个request后,最应该先分析head了。
画个图看看:
ngx_list_t
pool
|
nalloc
|
size
|
part
|
last
|
<!--[if gte vml 1]> <![endif]-->
ngx_list_part_s
<!--[if gte vml 1]> <![endif]-->
ngx_table_elt_t
图表 <!--[if supportFields]>SEQ
图表 /* ARABIC
<![endif]-->5<!--[if supportFields]><![endif]-->-nginx封装的list结构
当然ngx_table_elt_t结构只是head里的ngx_list_t所用,实际上每个元素可以是任意类型,由elts指针所指。
ngx_list_t里的nalloc和size,分别表示元素桶的数量和每个元素的大小,所以这里大家可以看出,该数据结构是定长内存组成的了吧。元素桶这个概念,大家可以理解为ngx_list_part_s结构,这个结构里可以保存多个元素,元素数量由nelts决定。每个元素是由ngx_list_part_s->elts指向的,长度为ngx_list_t->size
给段遍历list取得每个head的代码看看:
part =&r->upstream->headers_in.headers.part;
header= part->elts;
for (i= 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if(header[i].hash == 0) {
continue;
}
//header[i].key就是head的名字,header[i].value就是head的值了
}
开发nginx module,可以非常灵活的实现自己需要的功能。很多时候我们需要修改源码才能实现自己的特定功能,比如,nginx的thumbnail resize功能,不支持实时的对每一个请求按照指定的size来压缩,实际上在它的代码中完全可以做到,只需要稍微修改下源码而已。
又如upstream,nginx把它当足是个proxy机制,如果我们想让它只做为异步网络调用,也只需要在upstream_process_header里做些改动。
在需要某些特殊的功能时,我们最应该首先阅读nginx的源码,通常都会发现很多意想不到的收获。
Nginx的性能确实不错,对http协议的处理也很高效,在我们需要高性能webserver时,应该去优先考虑它。
分享到:
相关推荐
nginx module开发指南(中文版)
**最牛**的还是由淘宝的工程师清无(王晓哲)和春来(章亦春)所开发的[nginx_lua_module]可以将Lua语言嵌入到Nginx配置中,从而利用Lua极大增强了Nginx本身的编程能力,甚至可以不用配合其它脚本语言(如PHP或...
nginx web server的扩展模块开发文档。
也是IMAP/POP3/SMTP代理服务器,是由俄罗斯人lgor Sysoev开发,支持模块加载和卸载,其中upload_module和upload_progress_module就是第三方开发的模块,并没有加入到Nginx的源码中,upload_module是上传文件到服务器...
Sticky是nginx的一个模块,它是基于cookie的一种nginx的负载均衡解决方案,通过分发和识别cookie,来使同一个客户端的请求落在同一台服务器上,默认标识名为route (a)客户端首次发起访问请求,nginx接收后,发现...
在Nginx的模块编写过程中,时常苦于文档的不足,而源代码中又没多少注释。感谢Emiller的这篇英文文档带我入门。这次我翻译的是一至三章,希望能给中国广大Nginxer能有帮助,对于一般的应用来说,前三章的内容就足够...
OpenResty 是一个强大的 Web 应用服务器,Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,更主要的是在性能方面,OpenResty可以 快速构造出足以胜任 10K 以上并发连接响应的超高性能 Web ...
nginx后端节点健康检查模块插件,非常好用,淘宝开发。
第二部分针对中级读者,以一个例子为主线,告诉读者如何开发一个模块,这部分读者不需要深入了解Nginx的细节,只需要知道如何实现一个基本的模块。第三部分针对高级读者,这是本书的重点,彻底解析Nginx架构,深入...
nginx_upstream_check_module-master,淘宝技术团队开发的,适用1.11
test-nginx, 面向 Nginx C 模块和 OpenResty Lua库开发的数据驱动测试 电子邮件名称Test::Nginx - Nginx MODULE 和 Nginx/openresty库和应用程序的数据驱动测试脚手架 table-内容NAME描述用户指南使用 Test::Nginx ...
因为是懒人直接采用了整个安装openresty的策略,当然你也可以单独安装nginx,再安装lua,然后nginx_lua_module。安装了openresty之后,开始写简单的测试,其实在openresty的github上也有相关的例子,可以对着例子...
借助淘宝技术团队开发的nginx模快nginx_upstream_check_module来检测后方realserver的健康状态,如果后端服务器不可用,则会将其踢出upstream,所有的请求不转发到这台服务器。当期恢复正常时,将其加入upstream。 ...
特性很牛叉,可自行百度查看,这里主要是示范一下,如何在Nginx下安装lua-nginx-module模块 当然,如果你之前没有安装过Nginx,而且嫌安装麻烦,可直接下载openresty安装简单快捷,...
*) Bugfix: in cache key calculation if internal MD5 implementation was used; the bug had appeared in 1.0.4. *) Bugfix: the module ngx_http_mp4_module sent incorrect "Content-Length" response header ...
tcp-nginx-module 使用nginx作为通用TCP服务器框架 描述 写这些的动机是为了使用nginx作为一个通用的TCP服务器框架,所以叫ngx tcp。ngx tcp.jpg说明了这个框架。大部分代码是从nginx邮件模块修改而来的。我开发了...
此模块是基于openresty开发(或者nginx安装了ngx_lua_module模块, 建议使用openresty)
stats-nginx-module-统计持续聚合。 地位 此Nginx模块仍在试验和开发中。 概要 http { stats_format main '${remote_addr:c} $bytes_sent ${body_bytes_sent:a}' ; stats_zone $...
--add-module=/path/to/ngx_sqlite $ make $ make install ```摘要```nginx user www www; worker_processes 4; events { worker_connections 1024; } http { include mime.types; default_...