- 浏览: 219892 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
DILIGENT203:
最后绿色的一段话没有看明白,还是不明白nginx为什么要广播c ...
Nginx事件处理(epoll) -
烟雨遥_sun:
可否考虑用长连接?表示正在用这个多线程连接对数据库的,遇到了内 ...
MySQL C API的内存泄露问题 -
gsnumen:
...
Nginx的upstream模块和反向代理(一) -
zivon:
...
Nginx的upstream模块和反向代理(一)
Nginx因其出色的处理并发连接的能力,越来越多地作为一个反向代理服务器被使用。所谓反向代理,即把Nginx置于用户接入的最前端,监听用户发来的请求,并把它们转发给相应的后端服务器来处理具体的请求。后端服务器可以是缓存服务器(如Squid)或是处理动态/静态请求的服务器(如apache/Nginx/lighttpd),在这里不作深入讨论。本文对Nginx在upstream服务器的设置和"proxy_pass"的功能在代码层面进行分析,并讨论在实际生产环境的出现的问题。
upstream模块主要有两个命令,"upstream"和"server"。在配置文件里面,大概的结构是这样的:
http{ #... upstream backend_name{ server xx.xx.xx.xx:xx weight=2 max_fails=3; server www.xxx.com weight=1; server unix://xxx/xxx; #... } #... }
其中在"http"命令被解析的时候,会调用ngx_http_block()函数,而在这个函数里面会再次解析配置文件,从而发现"upstream"命令,调用它的set(),即ngx_http_upstream()函数。这个函数是这样的:
ngx_http_upstream(...) { //获取upstream服务器组的名字,即upstream命令的参数 //... //这个函数初始化一个ngx_http_upstream_srv_conf_t类型,并存入umcf->upstreams数组里面 //注意:这个函数还有一个作用,就是在"proxy_pass"的时候从umcf->upstreams找出match的upstream服务器组(不知道为啥要放在同一个函数里><) uscf = ngx_http_upstream_add(...); ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); //继承main_conf http_ctx = cf->ctx; ctx->main_conf = http_ctx->main_conf; //创建新的srv_conf ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); //把ngx_http_upstream_add()创建的ngx_http_upstream_srv_conf_t存入srv_conf ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf; //虽然uscf本身是一个srv_conf,但是它的成员中也有srv_conf,指向全局的srv_conf uscf->srv_conf = ctx->srv_conf; //创建loc_conf ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); //for each NGX_HTTP_MODULE for (;;){ //if this module has create_srv_conf() ... mconf = module->create_srv_conf(cf); ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf; //if this module has create_loc_conf() ... mconf = module->create_loc_conf(cf); ctx->loc_conf[ngx_modules[m]->ctx_index] = mconf; } cf->ctx = ctx; cf->cmd_type = NGX_HTTP_UPS_CONF; //继续解析server的配置 rv = ngx_conf_parse(cf, NULL); }
继续解析"server"的配置行,每行调用一次:
ngx_http_upstream_server(...) { ngx_http_upstream_server_t *us; us = ngx_array_push(uscf->servers); //获取第一个参数,即url u.url = value[1]; u.default_port = 80; ngx_parse_url(cf->pool, &u); //解析"server"后面的其他参数 //... //存入us us->addrs = u.addrs; us->naddrs = u.naddrs; us->weight = weight; us->max_fails = max_fails; us->fail_timeout = fail_timeout; }
在ngx_http_core_module的server {...} block的 /location {...} block里面,如果有"proxy_pass"指令,就会调用它的set(),即ngx_http_proxy_pass():
ngx_http_proxy_pass(...) { ngx_http_proxy_loc_conf_t *plcf = conf; //获取当前的location,即在哪个location配置的"proxy_pass"指令 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); //设置loc的handler,这个clcf->handler会在ngx_http_update_location_config()里面赋予r->content_handler,从而在NGX_HTTP_CONTENT_PHASE里面调用这个handler,即ngx_http_proxy_handler。 clcf->handler = ngx_http_proxy_handler; //如果"proxy_pass"第一个参数(即url)里面的变量 //... //根据http/https设置port和add(位移) //... u.url.len = url->len - add;//设置url的长度,除去http://(https://) u.url.data = url->data + add;//设置url,除去http://(https://),比如原来是"http://backend1",现在就是"backend1" u.default_port = port;//默认port u.uri_part = 1; u.no_resolve = 1;//不要resolve这个url的域名 //这儿从已经定义的upstream服务器组里面找到名字match的组 plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); //设置plcf的成员 //... //设置location的名字 plcf->location = clcf->name; //... }
那么,当有请求访问到特定的location的时候(假设这个location配置了proxy_pass指令),跟其他请求一样,会调用各个phase的checker和handler,到了NGX_HTTP_CONTENT_PHASE的checker,即ngx_http_core_content_phase()的时候,会调用r->content_handler(r),即ngx_http_proxy_handler。
ngx_http_proxy_handler(r) { ngx_http_upstream_t *u; //生成一个新的ngx_http_upstream_t,赋值给r->upstream //... ngx_http_upstream_create(r); //设置r->ctx[ngx_http_proxy_module.ctx_index] = ctx ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t)); ngx_http_set_ctx(r, ctx, ngx_http_proxy_module); //获取ngx_http_proxy_loc_conf_t plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); //... //获取upstream server的信息,如标签、服务器ip、port等 u->conf = &plcf->upstream; //生成发送到上游服务器的请求缓冲(或者一条缓冲链) u->create_request = ngx_http_proxy_create_request; //在后端服务器被重置的情况下(在create_request被第二次调用之前)被调用 u->reinit_request = ngx_http_proxy_reinit_request; //处理上游服务器回复的第一个bit,时常是保存一个指向上游回复负载的指针 u->process_header = ngx_http_proxy_process_status_line; //在客户端放弃请求的时候被调用 u->abort_request = ngx_http_proxy_abort_request; //在Nginx完成从上游服务器读入回复以后被调用 u->finalize_request = ngx_http_proxy_finalize_request; //... u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t)); u->pipe->input_filter = ngx_event_pipe_copy_input_filter; ngx_http_read_client_request_body(r, ngx_http_upstream_init); //没有special response return NGX_DONE;//-4 }
ngx_http_read_client_request_body()里面会调用post_handler()即ngx_http_upstream_init():
ngx_http_upstream_init(r) { //... //如果是edge trigger事件机制(epoll/kqueue),添加一个NGX_WRITE_EVENT //这个事件用来检查与用户的连接是否断开 ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT); ngx_http_upstream_init_request(r); }
ngx_http_upstream_init_request(r) { ngx_http_upstream_t *u; u = r->upstream; //设置r->read_event_handler和r->write_event_handler为事件调用函数 r->read_event_handler = ngx_http_upstream_rd_check_broken_connection; r->write_event_handler = ngx_http_upstream_wr_check_broken_connection; //把用户来的request内容(如请求行,headers)放到r->upstream的成员变量中 //如r->upstream->url存放请求的url //把请求(包括请求行,headers)放入r->upstream->request_bufs这个buf chain u->create_request(r); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); //初始化u->output(ngx_output_chain_ctx_s类型) u->output.alignment = clcf->directio_alignment; u->output.pool = r->pool; u->output.bufs.num = 1; u->output.bufs.size = clcf->client_body_buffer_size; u->output.output_filter = ngx_chain_writer; u->output.filter_ctx = &u->writer; u->writer.pool = r->pool; //创建state(ngx_http_upstream_state_t类型) //... //添加一个cleanup处理方法 ngx_http_cleanup_add(r,0); cln->handler = ngx_http_upstream_cleanup; cln->data = r; u->cleanup = &cln->handler; //获取ngx_http_upstream_srv_conf_t uscf = u->conf->upstream; ngx_http_upstream_connect(r,u); }
ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) { u->state = ngx_array_push(r->upstream_states); //获取时间,存入u->state //... //创建一个socket,调用ngx_get_connection()获取一个空闲的connection并初始化 //把这个connection添加到事件监听,并bind()以及connect()到peer //不等建立连接,直接返回,一般返回值EINPROGRESS,表示正在进行中 rc = ngx_event_connect_peer(&u->peer); c = u->peer.connection; c->data = r; c->write->handler = ngx_http_upstream_handler; c->read->handler = ngx_http_upstream_handler; u->write_event_handler = ngx_http_upstream_send_request_handler; u->read_event_handler = ngx_http_upstream_process_header; c->sendfile &= r->connection->sendfile; u->output.sendfile = c->sendfile; //init or reinit the ngx_output_chain() and ngx_chain_writer() contexts //... //rc == NGX_AGAIN,连接进行中 ngx_add_timer(c->write, u->conf->connect_timeout); //rc == NGX_OK,连接成功 ngx_http_upstream_send_request(); }
返回之后就进入了ngx_http_finalize_request() --> ngx_http_finalize_connection() --> ngx_http_close_request(),但是不会关闭客户端的连接。然后触发一个EPOLLOUT事件(发生在和用户的连接),进入ngx_http_request_handler()。这个事件是在ngx_http_upstream_init()里面添加的。而这个事件处理函数在ngx_http_process_request()里面设置。这个函数很简单:
ngx_http_request_handler(ngx_event_t *ev) { c = ev->data; r = c->data; //if... r->write_event_handler(r); //else... r->read_event_handler(r); //subrequest... ngx_http_run_posted_requests(c); }
ngx_http_upstream_init_request()里面设置了r->read_event_handler和r->write_event_handler,它们都指向ngx_http_upstream_check_broken_connection,那么在上面的函数里,就会调用这个函数。它主要是检查与用户的连接是否断开。
ngx_http_upstream_check_broken_connection(r,ev) { c = r->connection; u = r->upstream; n = recv(c->fd, buf, 1, MSG_PEEK); //... //正常err应该是NGX_EAGAIN(11) err = ngx_socket_errno; //... }
处理完之后,另外一个事件也同时触发(在epoll处理事件循环里),是之前跟upstream建立的连接的写事件(向upstream服务器发送)。调用c->write->handler即ngx_http_upstream_handler()。这个是在之前ngx_http_upstream_connect()里面设置的:
ngx_http_upstream_handler(ev) { c = ev->data; r = c->data; u = r->upstream; c = r->connection; //if... u->write_event_handler(r, u); //else... u->read_event_handler(r, u); //subrequest... ngx_http_run_posted_requests(c); }
从ngx_http_upstream_connect(),我们知道u->write_event_handler = ngx_http_upstream_send_request_handler,这个函数主要调用了ngx_http_upstream_send_request()函数:
ngx_http_upstream_send_request(r,u) { c = u->peer.connection; //向后端发送请求 rc = ngx_output_chain(&u->output, u->request_sent ? NULL : u->request_bufs); u->write_event_handler = ngx_http_upstream_dummy_handler; }
当后端(upstream)服务器处理完请求之后,会发回响应,这个时候事件被触发。再次调用ngx_http_upstream_handler(),这次调用u->read_event_handler,即ngx_http_upstream_process_header():
ngx_http_upstream_process_header(r,u) { //... for ( ;; ) { n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); //u->process_header = ngx_http_proxy_process_status_line //还调用了ngx_http_proxy_process_header(),处理回复的headers rc = u->process_header(r); } ngx_http_upstream_process_headers(); //如果subrequest_in_memory == 0 ngx_http_upstream_send_response(r, u); //else... //... }
ngx_http_upstream_send_response(r,u) { //调用ngx_http_top_header_filter即ngx_http_header_filter() //ngx_http_header_filter()中会调用ngx_http_write_filter() //ngx_http_write_filter()遍历所有chain,然后输出所有数据 rc = ngx_http_send_header(r); //... u->read_event_handler = ngx_http_upstream_process_upstream; r->write_event_handler = ngx_http_upstream_process_downstream; ngx_http_upstream_process_upstream(r, u); }
ngx_http_upstream_process_upstream(r,u) { c = u->peer.connection; //如果没有timeout // ngx_event_pipe(u->pipe, 0); }
发表评论
-
Nginx upstream 长连接
2011-01-31 15:51 21823Nginx upstream目前只有短 ... -
Nginx的upstream模块和反向代理(二)
2011-01-31 14:53 4020上一篇把upstream的配置和初始化的代码稍微分析了一下。本 ... -
Nginx Proxy Cache分析
2011-01-19 15:26 10993本文从几个部分来详细 ... -
Nginx Proxy Cache的slab page内存缓存机制
2011-01-15 11:39 6075Nginx的内存缓存是通过slab pool来实现的,但是目前 ... -
Nginx spinlock互斥锁
2011-01-13 19:01 3605在nginx里面,定义了一个spinlock,来同步父子进程间 ... -
proxy_cache
2011-01-12 21:49 0http://blogold.chinaunix.net/u3 ... -
Nginx http端口监听
2011-01-04 22:41 5416server {} block里面的"l ... -
Nginx研究进阶(待完成)
2011-01-04 10:58 01(minor). ports (ngx_http_conf_ ... -
Nginx的HTTP请求处理
2010-12-30 10:05 7426Nginx在7层负载交换、反向代理服务领域使用比较广泛。 ... -
Nginx事件处理(epoll)
2010-12-30 09:51 14074事件处理是Nginx处理请求的核心,每个子进程在ngx_wor ... -
Nginx父子进程的创建及主体工作函数
2010-12-30 09:28 3307根据Nginx(0.7.67版本)的代码,对Nginx基本的进 ...
相关推荐
Nginx作为一款高性能的HTTP和反向代理服务器,经常用于负载均衡,将来自客户端的请求分发到后端的一组服务器上。upstream模块是实现这一功能的关键部分,它允许我们定义一组服务器,Nginx会根据预设的策略(如轮询、...
Nginx作为一款高性能的反向代理服务器和HTTP缓存服务器,其内置的Upstream模块提供了强大的负载均衡能力。本资源包“nginx-upstream-fair”正是为了帮助用户快速配置Nginx的Upstream负载均衡而设计。 首先,我们来...
这个模块适用于任何依赖Nginx反向代理和负载均衡的场景,特别是大型网站、分布式系统和微服务架构,确保服务高可用性和用户体验。 综上所述,`nginx_upstream_check_module`是Nginx的一个强大扩展,通过健康检查...
首先,Nginx是一款高性能的反向代理服务器,常用于负载均衡,通过upstream模块配置多个后端服务器,以提高服务的可用性和响应速度。监控Nginx upstream服务器的状态对于保障整个系统稳定运行至关重要。 Zabbix提供...
5. **负载均衡和健康检查**:如果你有多个Tomcat实例,可以在Nginx配置中使用`upstream`模块进行负载均衡。同时,可以设置健康检查,确保只有健康的后端服务器接收请求。 通过以上步骤,你已成功配置Nginx作为...
Nginx是一款高性能的HTTP和反向代理服务器,广泛用于网站的负载均衡和内容分发。在Nginx中,upstream模块是处理后端服务器群的重要组件,它负责将客户端请求转发到适当的服务器上。"nginx-upstream-fair-master.zip...
为了满足动态负载均衡的需求,nginx提供了丰富的upstream模块,其中,nginx_upstream_hash模块是其一,它允许我们基于特定的请求参数来分配请求到不同的后端服务器,实现更为灵活的负载策略。本文将详细介绍nginx_...
Nginx 是一款高性能的 HTTP 和反向代理服务器,常用于网站负载均衡和内容分发。在 Nginx 的配置中,upstream 模块扮演了重要的角色,它允许我们定义一组服务器,根据不同的策略将请求分发到这些服务器上。`nginx-...
在标题和描述中提到的"Nginx_upstream_hash-0.3.1.tar.gz"是一个Nginx的第三方模块,它扩展了upstream模块的功能,引入了基于URL哈希的分发策略。这个模块的版本为0.3.1,表明它是该功能的一个特定实现。 这个模块...
Nginx是一款高性能的HTTP和反向代理服务器,广泛应用于网站负载均衡和静态内容处理。在处理后端服务器的负载均衡时,Nginx提供了多种策略,其中之一就是"Fair"(公平)算法。Fair算法的核心思想是根据后端服务器的...
作为一个反向代理服务器,Nginx 能够在客户端和后端应用服务器之间起到中介作用,从而实现负载均衡、缓存、安全过滤等多种功能。以下我们将深入探讨Nginx的反向代理特性和实际应用。 1. **反向代理基本原理** 反向...
在本教程中,我们将探讨如何在Windows环境下配置Nginx进行反向代理和负载均衡,以及Nginx的基本操作。** ### 一、安装Nginx 1. 解压"nginx-1.19.0"压缩包到您希望的安装目录。 2. 进入"conf"目录,编辑`nginx.conf`...
1.1 Nginx不仅是一个出色的web软件,其七层代理和负载均衡也是相当出色。Nginx做前端代理,当用户请求服务时,可以根据url进行判断,然后分配到不同的后台webserver上。 1.2 Nginx的负载均衡实现原理:首先在http...
Nginx是一款高性能的HTTP和反向代理服务器,也是一款邮件代理服务器,广泛应用于网站的负载均衡和静态内容服务。其轻量级、高效的特性使其在处理高并发连接时表现出色,是许多大型网站和应用的首选Web服务器。 标题...
### Nginx 反向代理与负载...总结来说,Nginx 的反向代理和负载均衡功能强大且灵活,能够有效地帮助开发者构建高效、稳定的应用系统。通过对 Nginx 的深入理解和合理配置,可以显著提高 Web 服务的整体性能和用户体验。
在IT行业中,Nginx是一款广泛应用的高性能反向代理服务器,因其高效稳定和灵活的配置而备受青睐。这里我们关注的是一个包含特定模块的Nginx配置:`nginx1.16`,`nginx-upstream-check-module-master` 和 `nginx-...
Nginx凭借其高效的反向代理和负载均衡能力,成为构建高性能网站和微服务架构的关键组件。通过合理的配置和优化,可以大大提高系统的稳定性和响应速度。同时,Nginx的集群搭建和Session管理策略也是保障服务高可用性...