- 浏览: 219491 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
DILIGENT203:
最后绿色的一段话没有看明白,还是不明白nginx为什么要广播c ...
Nginx事件处理(epoll) -
烟雨遥_sun:
可否考虑用长连接?表示正在用这个多线程连接对数据库的,遇到了内 ...
MySQL C API的内存泄露问题 -
gsnumen:
...
Nginx的upstream模块和反向代理(一) -
zivon:
...
Nginx的upstream模块和反向代理(一)
Nginx在7层负载交换、反向代理服务领域使用比较广泛。Nginx的结构也比较简单,除了底层几个核心的模块(如ngx_core_module,ngx_event_core_module,ngx_errlog_module等)之外,其它的主要是基于上述核心模块的http和mail的模块组,负责处理相关服务。而这些模块也可以在编译的时候被enable/disable,取决于对实际功能的需求。在这里,我来分析一下Nginx用的最多的功能,即处理http请求的工作流程。
在事件处理的分析中,提到过当有http请求过来时事件的触发和处理过程。我们知道,在一个子进程accept()请求之后,会调用ngx_http_init_connection()函数。这个函数会添加一个读事件,并设置其handler为ngx_http_init_request()。但是,对于http模块的载入以及初始化,却是要从http_block()开始。在父进程(master process)调用ngx_init_cycle()的时候,会调用一次ngx_conf_parse()函数(先不在这里分析),这个时候(解析到了"http {...}" block),ngx_http_module模块的set()函数即ngx_http_block()就被调用。
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { /* the main http context */ //它里面只有三个成员:(void**)main_conf,(void**)srv_conf和(void**)loc_conf,注意它们是双层指针 //每个NGX_HTTP_MODULE模块都有一个main_conf[i],srv_conf[i]和loc_conf[i]指向相应的上下文,但不是每个都会用到。 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); *(ngx_http_conf_ctx_t **) conf = ctx; /* 清点NGX_HTTP_MODULE类型模块个数并给每个模块设定索引(index) */ ngx_http_max_module = 0; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } ngx_modules[m]->ctx_index = ngx_http_max_module++; } //main_conf在http{...}里面的所有上下文中都是一致的 //分配内存,个数为http模块的个数 ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); //用来合并server{...}里面的srv_conf //分配内存,个数为http模块的个数 ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); //用来合并 /loc {...} 里面的loc_conf //分配内存,个数为http模块的个数 ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); //如有定义的话,调用每个http模块里面的create_main_conf(),create_srv_conf()和create_loc_conf() for (;;) { //如果是NGX_HTTP_MODULE模块 module = ngx_modules[m]->ctx;//module的上下文,为ngx_http_module_t类型 mi = ngx_modules[m]->ctx_index;//在http模块组里的索引 //如有的话,就调用每个模块的下列函数,并存入ctx相应的索引位 //一般在 http {...}里面而不在 server {...}里面有命令的模块有create_main_conf() //一般在 server {...}里面而不在 /loc {...}里面有命令的模块有create_srv_conf() //一般在 /loc {...}里面有命令的模块有create_loc_conf() ctx->main_conf[mi] = module->create_main_conf(cf);//创建模块自定的上下文,存入相应位置 ctx->srv_conf[mi] = module->create_srv_conf(cf);//创建模块自定的srv上下文,存入相应位置 ctx->loc_conf[mi] = module->create_loc_conf(cf);//创建模块自定的loc上下文,存入相应位置 } pcf = *cf; cf->ctx = ctx;//把ctx放入cf for (;;) { //调用每个NGX_HTTP_MODULE模块的preconfiguration()函数 //一般模块的preconfiguration()作用是添加一些模块要用的变量到ngx_http_core_main_conf_t的hash表variables_keys module->preconfiguration(cf); } /* parse inside the http{} block */ cf->module_type = NGX_HTTP_MODULE; cf->cmd_type = NGX_HTTP_MAIN_CONF; rv = ngx_conf_parse(cf, NULL);//会调用指令的set()函数,如果有嵌套的block会继续调用ngx_conf_parse()。大部分的http{}(但在server{}外)的指令都是给ngx_http_core_loc_conf_t的成员赋值。 //获取ngx_http_core_module(定义一些http公用的命令和变量)的main_conf cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; cscfp = cmcf->servers.elts;//cmcf->servers在ngx_http_core_create_main_conf里面初始化并分配内存,并在server{} block里面读取配置并设置相关变量 for (;;) { //调用每个NGX_HTTP_MODULE模块的init_main_conf(),调用ngx_http_merge_servers() rv = module->init_main_conf(cf, ctx->main_conf[mi]);//对main_conf上下文的一些成员变量做初始化 rv = ngx_http_merge_servers(cf, cmcf, module, mi);//每个server{} block都有一个srv_conf(继承ngx_http_core_module的srv_conf),且其ctx变量指向一组main_conf[],srv_conf[]和loc_conf[],储存了各个模块的main_conf,srv_conf,loc_conf(但是只有在server{}中调用模块的指令,才会用到这些xxx_conf) } /* create location trees */ //给每个server创建location tree for (s = 0; s < cmcf->servers.nelts; s++) { clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];//每个srv(虚拟主机)的ngx_http_core_loc_conf_t //location根据字母顺序排序 ngx_http_init_locations(cf, cscfp[s], clcf); //建立静态location树(三叉树) //在http://blog.csdn.net/benbendy1984/archive/2010/11/18/6019336.aspx有简单介绍 ngx_http_init_static_location_trees(cf, clcf); } //初始化几个phase的handler(分配内存) if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } //初始化header的hash table if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } for (;;) { //注册每个模块对应phase的处理函数 //这儿的处理函数是通用的(如http模块的所有location) //特定location的处理函数在指令(directive)的set()里面设置 module->postconfiguration(cf); } //设置pre-defined variables的get_handler等,并放入hash table里 if (ngx_http_variables_init_vars(cf) != NGX_OK) { return NGX_CONF_ERROR; } //注册各http模块phase的checker,并把postconfiguration注册的handler都放到cmcf的phase_engine,phase_engine把cheker和对应的handler一同放入数组,并通过next变量链接,供之后处理请求的时候按phase顺序调用。 //在http://simohayha.iteye.com/blog/670326有比较详细的介绍 if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } //把server_name储存在hash table里,最后给每个listening socket注册ls->handler=ngx_http_init_connection; //详见http://blog.csdn.net/ccdd14/archive/2010/09/12/5878459.aspx if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) { return NGX_CONF_ERROR; } return NGX_CONF_OK; }
当nginx收到请求的时候,就会调用(1)ngx_http_init_request,开始了对请求的处理过程。
static void ngx_http_init_request(ngx_event_t *rev) { //获取event的connection c = rev->data; //如果事件超时 //... hc = c->data; //给这个ngx_http_connection_t类型的指针分配内存 //... r = hc->request; //创建(re-init)一个新的request //... c->data = r; r->http_connection = hc; c->sent = 0; r->signature = NGX_HTTP_MODULE; /* find the server configuration for the address:port */ port = c->listening->servers;//在http_block()的时候就已经设置完成(在"listen"指令和ngx_http_optimize_servers函数里) r->connection = c; //如果有多个监听的addr if (port->naddrs > 1) { /* * there are several addresses on this port and one of them * is an "*:port" wildcard so getsockname() in ngx_http_server_addr() * is required to determine a server address */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_http_close_connection(c); return; } switch (c->local_sockaddr->sa_family) { default: /* AF_INET */ sin = (struct sockaddr_in *) c->local_sockaddr; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } addr_conf = &addr[i].conf; break; } } else { switch (c->local_sockaddr->sa_family) { default: /* AF_INET */ addr = port->addrs; addr_conf = &addr[0].conf; break; } } r->virtual_names = addr_conf->virtual_names; /* the default server configuration for the address:port */ cscf = addr_conf->core_srv_conf; r->main_conf = cscf->ctx->main_conf; r->srv_conf = cscf->ctx->srv_conf; r->loc_conf = cscf->ctx->loc_conf; //event的handler设为ngx_http_process_request_line rev->handler = ngx_http_process_request_line; //设置request的读事件 r->read_event_handler = ngx_http_block_reading; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); c->log->file = clcf->error_log->file; r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); //分配内存给variables r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); c->single_connection = 1; c->destroyed = 0; //对r的其它的初始化 r->main = r;//主请求 r->method = NGX_HTTP_UNKNOWN; r->headers_in.content_length_n = -1; r->headers_in.keep_alive_n = -1; r->headers_out.content_length_n = -1; r->headers_out.last_modified_time = -1; r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1; r->http_state = NGX_HTTP_READING_REQUEST_STATE; ctx = c->log->data; ctx->request = r; ctx->current_request = r; r->log_handler = ngx_http_log_error_handler; rev->handler(rev); //ngx_http_process_request_line }
下面,分别调用了下列函数,在这里不一一详细分析:
-->(2)ngx_http_process_request_line()//读取、解析请求行,并存入r的相应成员(如uri,args相关的重要指针)。
-->(3)ngx_http_process_request_headers()//把header line逐行解析并以key-value形式存入r。
-->(4)ngx_http_process_request()
-->(5)ngx_http_handler()
-->(6)ngx_http_core_run_phases()
void ngx_http_core_run_phases(ngx_http_request_t *r) { cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers;//拿到phase_engine里存放所有handler的数组 //如果不是内部定向(rewrite)的话,r->phase_handler = 0; //每个phase的checker在ngx_http_init_phase_handlers设置 //每个phase的handler在ngx_http_init_phase_handlers设置 //ph(ngx_http_phase_handler_s类型)的顺序:越早在postconfiguration()时注册handler的模块,它的handler在ph里面就越靠后被调用。 while (ph[r->phase_handler].checker) { //ngx_http_init_phase_handlers里面注册每个phase的checker和handler rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); if (rc == NGX_OK) { return; } } }
关于ngx_http_core_main_conf_t,它里面有两个成员,phase_engine和phases。其中phases是一个存放所有模块的在postconfiguration()函数里面注册的相关phase的handler。比如,ngx_http_index_module里面,就push了一个NGX_HTTP_CONTENT_PHASE的handler:ngx_http_index_handler。这个handler就被存放在ngx_http_core_main_conf_t的phases[NGX_HTTP_CONTENT_PHASE]这个数组里面。而phase_engine里面的handlers(ngx_http_phase_handler_s类型)则是一个checker和handler的函数对,next指向下一个phase在phase_engine里面的index。
比如说,最基本的一个处理过程:NGX_HTTP_FIND_CONFIG_PHASE --> NGX_HTTP_PREACCESS_PHASE --> NGX_HTTP_ACCESS_PHASE --> NGX_HTTP_CONTENT_PHASE。被调用的函数是:
//NGX_HTTP_FIND_CONFIG_PHASE的checker ngx_http_core_find_config_phase() { //调用ngx_http_core_find_static_location(),比较r的uri和config tree的名字,找到合适的/location,并更新r->loc_conf ngx_http_core_find_location(); //基于r->loc_conf,更新r的相应成员变量 //其中如果有一些location调用了某些模块的命令设置了特定的clcf->handler,r->content_handler = clcf->handler。在ngx_http_core_content_phase()里面就会被调用。 ngx_http_update_location_config(); //goto next handler即next phase r->phase_handler++; }
NGX_HTTP_PREACCESS_PHASE的checker是ngx_http_core_generic_phase(),即调用ph->handler(r)。在默认的模块配置里,NGX_HTTP_PREACCESS_PHASE类型的有ngx_http_limit_req_module和ngx_http_limit_zone_module两个模块,那么它们的ngx_http_limit_req_handler()和ngx_http_limit_zone_handler()就会被调用。在ngx_http_core_generic_phase()里面调用ph->handler(r)之后即控制下一个被调用的函数(是到下一个phase或者是本个phase的下一个handler)。
//NGX_HTTP_ACCESS_PHASE的checker ngx_http_core_access_phase() { rc = ph->handler(r); //根据rc来判断选择是下一个phase或者是本个phase的下一个handler //... }
在模块的默认配置里,NGX_HTTP_ACCESS_PHASE类型的是ngx_http_access_module和ngx_http_auth_basic_module,即ph->handler(r)调用了ngx_http_access_handler()和ngx_http_auth_basic_handler()
//NGX_HTTP_CONTENT_PHASE的checker ngx_http_core_content_phase() { //如果location有特定的handler //e.g. ngx_http_proxy_module的"proxy_pass"命令就会设置location的handler "ngx_http_proxy_handler" if (r->content_handler) { r->write_event_handler = ngx_http_request_empty_handler; ngx_http_finalize_request(r, r->content_handler(r)); return NGX_OK; } //没有location没有特定的handler rc = ph->handler(r); ph++; //跳到下一个NGX_HTTP_CONTENT_PHASE的handler或者下一个phase的checker if (ph->checker) { r->phase_handler++; return NGX_AGAIN; } //... }
默认的配置里有3个NGX_HTTP_CONTENT_PHASE类型的模块有ngx_http_static_module,ngx_http_index_module和ngx_http_autoindex_module,调用它们的ngx_http_static_handler(),ngx_http_index_handler()和ngx_http_autoindex_handler()。
通过实际运行的的debug信息,可以看到每个phase的checker被调用时候的handler的r->phase_handler(即index)。根据实际的情况(如根据不同location配置),不一定每个handler都会被调用(通过设置r->phase_handler = ph->next),甚至有时候会直接跳到另外一个phase(比如在ngx_http_index_handler()里面调用ngx_http_internal_redirect(),重新进入ngx_http_handler(),即重新把phase走一遍)。这些都是根据每个phase的handler的实现以及实际的请求来决定的。
关于每个phase的具体功能:
typedef enum { //0读取请求phase NGX_HTTP_POST_READ_PHASE = 0, //1这个阶段主要是处理全局的(server block)的rewrite。 NGX_HTTP_SERVER_REWRITE_PHASE, //2这个阶段主要是通过uri来查找对应的location,然后根据loc_conf设置r的相应变量 //e.g. 根据location内配置的具体命令设置r->content_handler,到NGX_HTTP_CONTENT_PHASE调用 NGX_HTTP_FIND_CONFIG_PHASE, //3这个主要处理location的rewrite NGX_HTTP_REWRITE_PHASE, //4post rewrite,这个主要是进行一些校验以及收尾工作,以便于交给后面的模块。 NGX_HTTP_POST_REWRITE_PHASE, //5比如流控这种类型的access就放在这个phase,也就是说它主要是进行一些比较粗粒度的access。 NGX_HTTP_PREACCESS_PHASE, //6这个比如存取控制,权限验证就放在这个phase,一般来说处理动作是交给下面的模块做的.这个主要是做一些细粒度的access NGX_HTTP_ACCESS_PHASE, //7一般来说当上面的access模块得到access_code之后就会由这个模块根据access_code来进行操作 NGX_HTTP_POST_ACCESS_PHASE, //8try_file模块,也就是对应配置文件中的try_files指令,可接收多个路径作为参数,当前一个路径的资源无法找到,则自动查找下一个路径 NGX_HTTP_TRY_FILES_PHASE, //9内容处理模块 NGX_HTTP_CONTENT_PHASE, //10log模块 NGX_HTTP_LOG_PHASE } ngx_http_phases;
Nginx的http上下文(在读取配置的时候设置):
发表评论
-
Nginx upstream 长连接
2011-01-31 15:51 21799Nginx upstream目前只有短 ... -
Nginx的upstream模块和反向代理(二)
2011-01-31 14:53 4009上一篇把upstream的配置和初始化的代码稍微分析了一下。本 ... -
Nginx Proxy Cache分析
2011-01-19 15:26 10978本文从几个部分来详细 ... -
Nginx Proxy Cache的slab page内存缓存机制
2011-01-15 11:39 6061Nginx的内存缓存是通过slab pool来实现的,但是目前 ... -
Nginx spinlock互斥锁
2011-01-13 19:01 3598在nginx里面,定义了一个spinlock,来同步父子进程间 ... -
proxy_cache
2011-01-12 21:49 0http://blogold.chinaunix.net/u3 ... -
Nginx http端口监听
2011-01-04 22:41 5401server {} block里面的"l ... -
Nginx研究进阶(待完成)
2011-01-04 10:58 01(minor). ports (ngx_http_conf_ ... -
Nginx的upstream模块和反向代理(一)
2010-12-30 10:16 44649Nginx因其出色的处理并发连接的能力,越来越多地作为一个反向 ... -
Nginx事件处理(epoll)
2010-12-30 09:51 14054事件处理是Nginx处理请求的核心,每个子进程在ngx_wor ... -
Nginx父子进程的创建及主体工作函数
2010-12-30 09:28 3298根据Nginx(0.7.67版本)的代码,对Nginx基本的进 ...
相关推荐
Nginx 中 HTTP 请求处理过程详解 Nginx 是一个流行的开源 Web 服务器软件,广泛应用于 Web 开发和生产环境中。理解 Nginx 中 HTTP 请求处理过程对于提高网站性能和可靠性至关重要。在本文中,我们将详细介绍 Nginx ...
在深入探讨Nginx请求头数据读取流程之前,首先理解HTTP请求报文的基本结构至关重要。一个典型的HTTP请求报文由三部分组成:请求行、请求头和请求体。请求行包含了请求方法(如GET、POST等)、请求URI和HTTP协议版本...
2. **处理模块(Handler Modules)**:处理模块主要负责处理HTTP请求,根据请求的类型和配置,生成相应的响应。当Nginx需要直接提供文件或转发请求到其他服务器时,就是由处理模块来完成的。如果多个处理模块映射到了...
1. **请求接收**:Nginx监听端口,接收到HTTP请求。 2. **解析请求**:解析请求头和请求体,根据配置文件中的`server`和`location`规则进行匹配。 3. **处理请求**:根据匹配结果,调用相应的HTTP模块处理请求。可能...
Nginx是一个高性能的HTTP和反向代理服务器,它常用于配置和管理网站的访问规则,包括处理跨域请求。 跨域请求是Web开发中常见的限制,由浏览器的同源策略实施。同源策略不允许一个域名下的文档或脚本获取或操作另一...
在Nginx中,一个完整的HTTP请求处理过程通常会经过11个处理阶段。每个阶段都允许多个HTTP模块参与,共同完成请求的处理。 Nginx中的event模块负责处理网络事件,包括接收请求、发送响应等。当接收到HTTP请求行和...
首先,Nginx是一个高性能的反向代理服务器和HTTP缓存服务器,它可以作为负载均衡器,将来自客户端的请求智能地分发到多个后端服务器,从而提高系统的响应速度和并发处理能力。负载均衡策略通常包括轮询、权重轮询、...
【跨域WebService请求】在现代Web开发中,由于浏览器的安全策略限制,JavaScript通常不能发起对不同源的HTTP请求,这被称为跨域问题。对于GET请求,可以通过JSONP(JSON with Padding)来解决,但POST请求则更为复杂...
1. **Nginx基础**:Nginx的工作原理是基于事件驱动的异步非阻塞模型,这使得它在处理高并发请求时表现出色。Nginx的主要组件包括主进程、工作进程、模块和配置文件。 2. **配置文件解析**:学习如何编写和理解Nginx...
1. **启用HTTP升级头**:WebSocket连接始于一个HTTP请求,通过`Upgrade`和`Connection`头字段来表明客户端想要升级到WebSocket协议。在Nginx配置中,我们需要确保这些头字段被正确处理。例如: ```nginx location ...
6. **直播推流**: 流媒体服务器接收到RTMP推流后,通过`nginx-http-flv-module`将RTMP流转换成HTTP FLV流,这样客户端就可以通过HTTP请求获取直播数据。推流端通常是编码软件,如OBS Studio,它将摄像头或桌面内容...
Nginx的配置主要通过主配置文件`nginx.conf`进行,其中包含多个服务器块(server blocks)来处理不同域名或端口的请求。每个服务器块可配置监听端口、虚拟主机、重定向规则、日志设置等。 3. **模块化设计** ...
这样配置后,Nginx可以处理HTTP请求,并对HTTPS请求进行加密处理。特别的,如果内网DNS直接将A记录指向了服务器,而服务器需要处理内网的HTTPS请求和外部DMZ区的HTTP请求,那么服务器就需要同时监听HTTP和HTTPS端口...
然后通过发送HTTP请求到Nginx服务器,测试图片处理功能是否正常工作。如果出现问题,可以查看Nginx的日志文件或者启用更详细的日志模式来进行调试。 通过这种方式,Nginx Lua不仅可以作为静态文件服务器,还可以...
- `pcre-8.45`:正则表达式库,用于处理URL匹配和其他HTTP请求解析。 2. **配置Nginx** 在解压后的`nginx-1.21.4`目录下,运行配置命令,指定`--add-module`参数指向`nginx-http-flv-module`的源代码路径,例如:...
此外,Nginx的事件驱动模型使其在处理高并发请求时表现出色。 【标签】"视频流媒体服务器"指出,这个组合主要用于搭建一个服务器,用于实时传输和播放视频流。在现代互联网应用中,视频流媒体服务广泛应用于在线...
在实际应用中,有时我们需要对HTTP请求进行一些自定义处理,比如向request的header中添加新的字段。这个过程涉及到Nginx的配置和HTTP协议的理解,接下来将详细介绍这一知识点。 首先,了解HTTP协议的基本结构。HTTP...