代理模式数据流处理:

//配置proxy_pass后,在 ngx_http_core_content_phase      里面指向该函数
/*
那么,当有请求访问到特定的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。
*/
static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx;
ngx_http_proxy_loc_conf_t *plcf; if (ngx_http_upstream_create(r) != NGX_OK) { //生成一个ngx_http_upstream_t结构,赋值到r->upstream
return NGX_HTTP_INTERNAL_SERVER_ERROR;
} ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
} //将这个上下文放到请求的ctx数组的ngx_http_proxy_module模块中,r->ctx[module.ctx_index] = c;
ngx_http_set_ctx(r, ctx, ngx_http_proxy_module); plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); u = r->upstream; if (plcf->proxy_lengths == NULL) {
} else {
if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {
//获取uri中变量的值,从而可以得到完整的uri,和ngx_http_proxy_pass配合阅读
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
} u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module; u->conf = &plcf->upstream; //为upstream准备各种请求的回调
u->create_request = ngx_http_proxy_create_request; //生成发送到上游服务器的请求缓冲(或者一条缓冲链),也就是要发给上游的数据
u->reinit_request = ngx_http_proxy_reinit_request;
//处理回调就是第一行的回调,第一行处理完后会设置为ngx_http_proxy_process_header,走下一步
u->process_header = ngx_http_proxy_process_status_line;
//一个upstream的u->read_event_handler 读事件回调被设置为ngx_http_upstream_process_header;,他会不断的读取数据,然后
//调用process_header对于FCGI,当然是调用对应的读取FCGI格式的函数了,对于代理模块,只要处理HTTP格式即可
u->abort_request = ngx_http_proxy_abort_request;
u->finalize_request = ngx_http_proxy_finalize_request;
r->state = 0; ----------------------------------------
//设置在有buffering的状态下,nginx读取upstream的回调,如果是FCGI,则是对应的回调ngx_http_fastcgi_input_filter用来解析FCGI协议的数据。
//如果我们要实现我们自己的协议格式,那就用对应的解析方式。
u->pipe->input_filter = ngx_http_proxy_copy_filter;
u->pipe->input_ctx = r; //buffering后端响应包体使用ngx_event_pipe_t->input_filter 非buffering方式响应后端包体使用ngx_http_upstream_s->input_filter ,在ngx_http_upstream_send_response分叉 u->input_filter_init = ngx_http_proxy_input_filter_init;
u->input_filter = ngx_http_proxy_non_buffered_copy_filter;
u->input_filter_ctx = r; u->accel = 1; if (!plcf->upstream.request_buffering
&& plcf->body_values == NULL && plcf->upstream.pass_request_body
&& (!r->headers_in.chunked
|| plcf->http_version == NGX_HTTP_VERSION_11))
{
r->request_body_no_buffering = 1;
} /*
在阅读HTTP反向代理模块(ngx_http_proxy_module)源代码时,会发现它并没有调用r->main->count++,其中proxy模块是这样启动upstream机制的:
ngx_http_read_client_request_body(r,ngx_http_upstream_init);,这表示读取完用户请求的HTTP包体后才会调用ngx_http_upstream_init方法
启动upstream机制。由于ngx_http_read_client_request_body的第一行有效语句是r->maln->count++,所以HTTP反向代理模块不能
再次在其代码中执行r->main->count++。 这个过程看起来似乎让人困惑。为什么有时需要把引用计数加1,有时却不需要呢?因为ngx_http_read- client_request_body读取请求包体是
一个同步I/O复用操作(需要epoll多次调度方能完成),ngx_http_upstream_init方法启用upstream机制也是一个类似于异步的操作,因此,
从理论上来说,每执行一次异步操作应该把引用计数加1,而异步操作结束时应该调用ngx_http_finalize_request方法把引用计数减1。另外,
ngx_http_read_client_request_body方法内是加过引用计数的,而ngx_http_upstream_init方法内却没有加过引用计数()。在HTTP反向代理模块中,它的ngx_http_proxy_handler方法中用“ngx_http_read- client_request_body(r,ngx_http_upstream_init);”
语句同时启动了两个异步操作,注意,这行语句中只加了一次引用计数。执行这行语句的ngx_http_proxy_handler方法返回时只调用
ngx_http_finalize_request方法一次,这是正确的。。
*/
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
} return NGX_DONE;
}

创建upstream 

/*
ngx_http_upstream_create 方法创建ngx_http_upstream_t结构体,其中的成员还需要各个HTTP模块自行设置。
启动upstream机制使用ngx_http_upstream_init方法
*/ //FastCGI memcached uwsgi scgi proxy模块的相关配置都放在该结构中
//ngx_http_request_t->upstream 中存取 //upstream资源回收在ngx_http_upstream_finalize_request
struct ngx_http_upstream_s { //该结构中的部分成员是从upstream{}中的相关配置里面(ngx_http_upstream_conf_t)获取的
//处理读事件的回调方法,每一个阶段都有不同的read event handler
//注意ngx_http_upstream_t和ngx_http_request_t都有该成员 分别在ngx_http_request_handler和ngx_http_upstream_handler中执行
//如果在读取后端包体的时候采用buffering方式,则在读取完头部行和部分包体后,会从置为ngx_http_upstream_process_upstream方式读取后端包体数据
////buffering方式,非子请求,后端头部信息已经读取完毕了,如果后端还有包体需要发送,则本端通过ngx_http_upstream_process_upstream该方式读取
//非buffering方式,非子请求,后端头部信息已经读取完毕了,如果后端还有包体需要发送,则本端通过ngx_http_upstream_process_non_buffered_upstream读取
//如果有子请求,后端头部信息已经读取完毕了,如果后端还有包体需要发送,则本端通过ngx_http_upstream_process_body_in_memory读取
ngx_http_upstream_handler_pt read_event_handler; //ngx_http_upstream_process_header ngx_http_upstream_handler中执行
//处理写事件的回调方法,每一个阶段都有不同的write event handler
//注意ngx_http_upstream_t和ngx_http_request_t都有该成员 分别在ngx_http_request_handler和ngx_http_upstream_handler中执行
ngx_http_upstream_handler_pt write_event_handler; //ngx_http_upstream_send_request_handler用户向后端发送包体时,一次发送没完完成,再次出发epoll write的时候调用 //表示主动向上游服务器发起的连接。 连接的fd保存在peer->connection里面
ngx_peer_connection_t peer;//初始赋值见ngx_http_upstream_connect->ngx_event_connect_peer(&u->peer); /*
当向下游客户端转发响应时(ngx_http_request_t结构体中的subrequest_in_memory标志住为0),如果打开了缓存且认为上游网速更快(conf
配置中的buffering标志位为1),这时会使用pipe成员来转发响应。在使用这种方式转发响应时,必须由HTTP模块在使用upstream机制前构造
pipe结构体,否则会出现严重的coredump错误
*/ //实际上buffering为1才通过pipe发送包体到客户端浏览器
ngx_event_pipe_t *pipe; //ngx_http_fastcgi_handler ngx_http_proxy_handler中创建空间 /* request_bufs决定发送什么样的请求给上游服务器,在实现create_request方法时需要设置它
request_bufs以链表的方式把ngx_buf_t缓冲区链接起来,它表示所有需要发送到上游服务器的请求内容。
所以,HTTP模块实现的create_request回调方法就在于构造request_bufg链表
*/ /* 这上面的fastcgi_param参数和客户端请求头key公用一个cl,客户端包体另外占用一个或者多个cl,他们通过next连接在一起,最终前部连接到u->request_bufs
所有需要发往后端的数据就在u->request_bufs中了,发送的时候从里面取出来即可,参考ngx_http_fastcgi_create_request*/
/*
ngx_http_upstream_s->request_bufs 的包体来源为 ngx_http_upstream_init_request 里面的u->request_bufs = r->request_body->bufs;然后在
ngx_http_fastcgi_create_request中会重新把发往后端的头部信息以及fastcgi_param信息填加到ngx_http_upstream_s->request_bufs中
*/ //向上游发送包体u->request_bufs(ngx_http_fastcgi_create_request),接收客户端的包体在r->request_body
//发往上游服务器的请求头内容放入该buf 空间分配在 ngx_http_proxy_create_request ngx_http_fastcgi_create_request
ngx_chain_t *request_bufs; //定义了向下游发送响应的方式
ngx_output_chain_ctx_t output; //输出数据的结构,里面存有要发送的数据,以及发送的output_filter指针
ngx_chain_writer_ctx_t writer; //参考ngx_chain_writer,里面会将输出buf一个个连接到这里。 writer赋值给了u->output.filter_ctx,见ngx_http_upstream_init_request
//调用ngx_output_chain后,要发送的数据都会放在这里,然后发送,然后更新这个链表,指向剩下的还没有调用writev发送的。 //upstream访问时的所有限制性参数,
/*
conf成员,它用于设置upstream模块处理请求时的参数,包括连接、发送、接收的超时时间等。
事实上,HTTP反向代理模块在nginx.conf文件中提供的配置项大都是用来设置ngx_http_upstream_conf_t结构体中的成员的。
上面列出的3个超时时间(connect_timeout send_imeout read_timeout)是必须要设置的,因为它们默认为0,如果不设置将永远无法与上游服务器建立起TCP连接(因为connect timeout值为0)。
*/ //使用upstream机制时的各种配置 例如fastcgi赋值在ngx_http_fastcgi_handler赋值来自于ngx_http_fastcgi_loc_conf_t->upstream
ngx_http_upstream_conf_t *conf; #if (NGX_HTTP_CACHE) //proxy_pache_cache或者fastcgi_path_cache解析的时候赋值,见ngx_http_file_cache_set_slot
ngx_array_t *caches; //u->caches = &ngx_http_proxy_main_conf_t->caches;
#endif /*
HTTP模块在实现process_header方法时,如果希望upstream直接转发响应,就需要把解析出的响应头部适配为HTTP的响应头部,同时需要
把包头中的信息设置到headers_in结构体中,这样,会把headers_in中设置的头部添加到要发送到下游客户端的响应头部headers_out中
*/
ngx_http_upstream_headers_in_t headers_in; //存放从上游返回的头部信息, //通过resolved可以直接指定上游服务器地址.用于解析主机域名 创建和赋值见ngx_http_xxx_eval(例如ngx_http_fastcgi_eval ngx_http_proxy_eval)
ngx_http_upstream_resolved_t *resolved; //解析出来的fastcgi_pass 127.0.0.1:9000;后面的字符串内容,可能有变量嘛。 ngx_buf_t from_client; /*
buffer成员存储接收自上游服务器发来的响应内容,由于它会被复用,所以具有下列多种意义:
a)在使用process_header方法解析上游响应的包头时,buffer中将会保存完整的响应包头:
b)当下面的buffering成员为1,而且此时upstream是向下游转发上游的包体时,buffer没有意义;
c)当buffering标志住为0时,buffer缓冲区会被用于反复地接收上游的包体,进而向下游转发;
d)当upstream并不用于转发上游包体时,buffer会被用于反复接收上游的包体,HTTP模块实现的input_filter方法需要关注它 接收上游服务器响应包头的缓冲区,在不需要把响应直接转发给客户端,或者buffering标志位为0的情况下转发包体时,接收包体的缓冲
区仍然使用buffer。注意,如果没有自定义input_filter方法处理包体,将会使用buffer存储全部的包体,这时buf fer必须足够大!它的大小
由ngx_http_upstream_conf_t结构体中的buffer_size成员决定
*/ //ngx_http_upstream_process_header中创建空间和赋值,通过该buf接受recv后端数据 //buf大小由xxx_buffer_size(fastcgi_buffer_size proxy_buffer_size memcached_buffer_size)
//读取上游返回的数据的缓冲区,也就是proxy,FCGI返回的数据。这里面有http头部,也可能有body部分。其body部分会跟event_pipe_t的preread_bufs结构对应起来。就是预读的buf,其实是i不小心读到的。
//该buf本来是接收头部行信息的,但是也可能会把部分或者全部包体(当包体很小的时候)收到该buf中
ngx_buf_t buffer; //从上游服务器接收的内容在该buffer,发往上游的请求内容在request_bufs中
//表示来自上游服务器的响应包体的长度 proxy包体赋值在ngx_http_proxy_input_filter_init
off_t length; //要发送给客户端的数据大小,还需要读取这么多进来。 /*
out_bufs在两种场景下有不同的意义:
①当不需要转发包体,且使用默认的input_filter方法(也就是ngx_http_upstream_non_buffered_filter方法)处理包体时,out bufs将会指向响应包体,
事实上,out bufs链表中会产生多个ngx_buf_t缓冲区,每个缓冲区都指向buffer缓存中的一部分,而这里的一部分就是每次调用recv方法接收到的一段TCP流。
②当需要转发响应包体到下游时(buffering标志位为O,即以下游网速优先),这个链表指向上一次向下游转发响应到现在这段时间内接收自上游的缓存响应
*/
ngx_chain_t *out_bufs;
/*
当需要转发响应包体到下游时(buffering标志位为o,即以下游网速优先),它表示上一次向下游转发响应时没有发送完的内容
*/
ngx_chain_t *busy_bufs;//调用了ngx_http_output_filter,并将out_bufs的链表数据移动到这里,待发送完毕后,会移动到free_bufs
/*
这个链表将用于回收out_bufs中已经发送给下游的ngx_buf_t结构体,这同样应用在buffering标志位为0即以下游网速优先的场景
*/
ngx_chain_t *free_bufs;//空闲的缓冲区。可以分配 /*
input_filter init与input_filter回调方法
input_filter_init与input_filter这两个方法都用于处理上游的响应包体,因为处理包体
前HTTP模块可能需要做一些初始化工作。例如,分配一些内存用于存放解析的中间状态
等,这时upstream就提供了input_filter_init方法。而input_filter方法就是实际处理包体的
方法。这两个回调方法都可以选择不予实现,这是因为当这两个方法不实现时,upstream
模块会自动设置它们为预置方法(上文讲过,由于upstream有3种处理包体的方式,所以
upstream模块准备了3对input_filter_init、input_filter方法)。因此,一旦试图重定义mput_
filter init、input_filter方法,就意味着我们对upstream模块的默认实现是不满意的,所以才
要重定义该功能。
在多数情况下,会在以下场景决定重新实现input_filter方法。
(1)在转发上游响应到下游的同时,需要做一些特殊处理
例如,ngx_http_memcached_ module模块会将实际由memcached实现的上游服务器返回
的响应包体,转发到下游的HTTP客户端上。在上述过程中,该模块通过重定义了的input_
filter方法来检测memcached协议下包体的结束,而不是完全、纯粹地透传TCP流。
(2)当无须在上、下游间转发响应时,并不想等待接收完全部的上游响应后才开始处理
请求
在不转发响应时,通常会将响应包体存放在内存中解析,如果试图接收到完整的响应后
再来解析,由于响应可能会非常大,这会占用大量内存。而重定义了input_filter方法后,可
以每解析完一部分包体,就释放一些内存。
重定义input_filter方法必须符合一些规则,如怎样取到刚接收到的包体以及如何稃放缓
冲区使得固定大小的内存缓冲区可以重复使用等。注意,本章的例子并不涉及input_filter方
法,读者可以在第12章中找到input_filter方法的使用方式。
*/
//处理包体前的初始化方法,其中data参数用于传递用户数据结构,它实际上就是下面的input_filter_ctx指针
//ngx_http_XXX_input_filter_init(如ngx_http_fastcgi_input_filter_init ngx_http_proxy_input_filter_init ngx_http_proxy_input_filter_init)
ngx_int_t (*input_filter_init)(void *data); /* 处理包体的方法,其中data参数用于传递用户数据结构,它实际上就是下面的input_filter_ctx指针,而bytes表示本次接收到的包体长度。
返回NGX ERROR时表示处理包体错误,请求需要结束,否则都将继续upstream流程 用来读取后端的数据,非buffering模式。ngx_http_upstream_non_buffered_filter,ngx_http_memcached_filter等。这个函数的调用时机:
ngx_http_upstream_process_non_buffered_upstream等调用ngx_unix_recv接收到upstream返回的数据后就调用这里进行协议转换,不过目前转换不多。
*/ //buffering后端响应包体使用ngx_event_pipe_t->input_filter 非buffering方式响应后端包体使用ngx_http_upstream_s->input_filter ,在ngx_http_upstream_send_response分叉
ngx_int_t (*input_filter)(void *data, ssize_t bytes); //ngx_http_xxx_non_buffered_filter(如ngx_http_fastcgi_non_buffered_filter ngx_http_proxy_non_buffered_copy_filter)
//用于传递HTTP模块自定义的数据结构,在input_filter_init和input_filter方法被回调时会作为参数传递过去
void *input_filter_ctx;//指向所属的请求等上下文 #if (NGX_HTTP_CACHE)
/*
Ngx_http_fastcgi_module.c (src\http\modules): u->create_key = ngx_http_fastcgi_create_key;
Ngx_http_proxy_module.c (src\http\modules): u->create_key = ngx_http_proxy_create_key;
*/ //ngx_http_upstream_cache中执行
ngx_int_t (*create_key)(ngx_http_request_t *r);
#endif
//构造发往上游服务器的请求内容
/*
create_request回调方法
create_request的回调场景最简单,即它只可能被调用1次(如果不启用upstream的
失败重试机制的话):
1)在Nginx主循环(这里的主循环是指ngx_worker_process_cycle方法)中,会定期地调用事件模块,以检查是否有网络事件发生。
2)事件模块在接收到HTTP请求后会调用HTIP框架来处理。假设接收、解析完HTTP头部后发现应该由mytest模块处理,这时会调用mytest模
块的ngx_http_mytest_handler来处理。
4)调用ngx_http_up stream_init方法启动upstream。
5) upstream模块会去检查文件缓存,如果缓存中已经有合适的响应包,则会直接返回缓存(当然必须是在使用反向代理文件缓存的前提下)。
为了让读者方便地理解upstream机制,本章将不再提及文件缓存。
6)回调mytest模块已经实现的create_request回调方法。
7) mytest模块通过设置r->upstream->request_bufs已经决定好发送什么样的请求到上游服务器。
8) upstream模块将会检查resolved成员,如果有resolved成员的话,就根据它设置好上游服务器的地址r->upstream->peer成员。
9)用无阻塞的TCP套接字建立连接。
10)无论连接是否建立成功,负责建立连接的connect方法都会立刻返回。
II) ngx_http_upstreamL init返回。
12) mytest模块的ngx_http_mytest_handler方法返回NGX DONE。
13)当事件模块处理完这批网络事件后,将控制权交还给Nginx主循环。
*/ //这里定义的mytest_upstream_create_request方法用于创建发送给上游服务器的HTTP请求,upstream模块将会回调它
//在ngx_http_upstream_init_request中执行 HTTP模块实现的执行create_request方法用于构造发往上游服务器的请求
//ngx_http_xxx_create_request(例如ngx_http_fastcgi_create_request)
ngx_int_t (*create_request)(ngx_http_request_t *r);//生成发送到上游服务器的请求缓冲(或者一条缓冲链) /*
reinit_request可能会被多次回调。它被调用的原因只有一个,就是在第一次试图向上游服务器建立连接时,如果连接由于各种异常原因失败,
那么会根据upstream中conf参数的策略要求再次重连上游服务器,而这时就会调用reinit_request方法了。图5-4描述了典型的reinit_request调用场景。
下面简单地介绍一下图5-4中列出的步骤。
1) Nginx主循环中会定期地调用事件模块,检查是否有网络事件发生。
2)事件模块在确定与上游服务器的TCP连接建立成功后,会回调upstream模块的相关方法处理。
3) upstream棋块这时会把r->upstream->request_sent标志位置为l,表示连接已经建立成功了,现在开始向上游服务器发送请求内容。
4)发送请求到上游服务器。
5)发送方法当然是无阻塞的(使用了无阻塞的套接字),会立刻返回。
6) upstream模块处理第2步中的TCP连接建立成功事件。
7)事件模块处理完本轮网络事件后,将控制权交还给Nginx主循环。
8) Nginx主循环重复第1步,调用事件模块检查网络事件。
9)这时,如果发现与上游服务器建立的TCP连接已经异常断开,那么事件模块会通知upstream模块处理它。
10)在符合重试次数的前提下,upstream模块会毫不犹豫地再次用无阻塞的套接字试图建立连接。
11)无论连接是否建立成功都立刻返回。
12)这时检查r->upstream->request_sent标志位,会发现它已经被置为1了。
13)如果mytest模块没有实现reinit_request方法,那么是不会调用它的。而如果reinit_request不为NULL空指针,就会回调它。
14) mytest模块在reinit_request中处理完自己的事情。
15)处理宪第9步中的TCP连接断开事件,将控制权交还给事件模块。
16)事件模块处理完本轮网络事件后,交还控制权给Nginx主循环。
*/ //与上游服务器的通信失败后,如果按照重试规则还需要再次向上游服务器发起连接,则会调用reinit_request方法
//下面的upstream回调指针是各个模块设置的,比如ngx_http_fastcgi_handler里面设置了fcgi的相关回调函数。
//ngx_http_XXX_reinit_request(ngx_http_fastcgi_reinit_request) //在ngx_http_upstream_reinit中执行
ngx_int_t (*reinit_request)(ngx_http_request_t *r);//在后端服务器被重置的情况下(在create_request被第二次调用之前)被调用 /*
收到上游服务器的响应后就会回调process_header方法。如果process_header返回NGXAGAIN,那么是在告诉upstream还没有收到完整的响应包头,
此时,对子本次upstream请求来说,再次接收到上游服务器发来的TCP流时,还会调用process_header方法处理,直到process_header函数返回
非NGXAGAIN值这一阶段才会停止 process_header回调方法process_header是用于解析上游服务器返回的基于TCP的响应头部的,因此,process_header可能会被多次调用,
它的调用次数与process_header的返回值有关。如图5-5所示,如果process_header返回NGX_AGAIN,这意味着还没有接收到完整的响应头部,
如果再次接收到上游服务器发来的TCP流,还会把它当做头部,仍然调用process_header处理。而在图5-6中,如果process_header返回NGX_OK
(或者其他非NGX_AGAIN的值),那么在这次连接的后续处理中将不会再次调用process_header。
process header回调场景的序列图
下面简单地介绍一下图5-5中列出的步骤。
1) Nginx主循环中会定期地调用事件模块,检查是否有网络事件发生。
2)事件模块接收到上游服务器发来的响应时,会回调upstream模块处理。
3) upstream模块这时可以从套接字缓冲区中读取到来自上游的TCP流。
4)读取的响应会存放到r->upstream->buffer指向的内存中。注意:在未解析完响应头部前,若多次接收到字符流,所有接收自上游的
响应都会完整地存放到r->upstream->buffer缓冲区中。因此,在解析上游响应包头时,如果buffer缓冲区全满却还没有解析到完整的响应
头部(也就是说,process_header -直在返回NGX_AGAIN),那么请求就会出错。
5)调用mytest模块实现的process_header方法。
6) process_header方法实际上就是在解析r->upstream->buffer缓冲区,试图从中取到完整的响应头部(当然,如果上游服务器与Nginx通过HTTP通信,
就是接收到完整的HTTP头部)。
7)如果process_header返回NGX AGAIN,那么表示还没有解析到完整的响应头部,下次还会调用process_header处理接收到的上游响应。
8)调用元阻塞的读取套接字接口。
9)这时有可能返回套接字缓冲区已经为空。
10)当第2步中的读取上游响应事件处理完毕后,控制权交还给事件模块。
11)事件模块处理完本轮网络事件后,交还控制权给Nginx主循环。
*/ //ngx_http_upstream_process_header和ngx_http_upstream_cache_send函数中调用
/*
解析上游服务器返回响应的包头,返回NGX_AGAIN表示包头还没有接收完整,返回NGX_HTTP_UPSTREAM_INVALID_HEADER表示包头不合法,返回
NGX ERROR表示出现错误,返回NGX_OK表示解析到完整的包头
*/ //ngx_http_fastcgi_process_header ngx_http_proxy_process_status_line->ngx_http_proxy_process_status_line(ngx_http_XXX_process_header)
//在ngx_http_upstream_process_header中执行
ngx_int_t (*process_header)(ngx_http_request_t *r); //处理上游服务器回复的第一个bit,时常是保存一个指向上游回复负载的指针
void (*abort_request)(ngx_http_request_t *r);//在客户端放弃请求的时候被调用 ngx_http_XXX_abort_request /*
当调用ngx_http_upstream_init启动upstream机制后,在各种原因(无论成功还是失败)导致该请求被销毁前都会调用finalize_request方
法。在finalize_request方法中可以不做任何事情,但必须实现finalize_request方法,否则Nginx会出现空指针调用的严重错误。 当请求结束时,将会回调finalize_request方法,如果我们希望此时释放资源,如打开
的句柄等,.那么可以把这样的代码添加到finalize_request方法中。本例中定义了mytest_
upstream_finalize_request方法,由于我们没有任何需要释放的资源,所以该方法没有完成任
何实际工作,只是因为upstream模块要求必须实现finalize_request回调方法
*/ //销毁upstream请求时调用 ngx_http_XXX_finalize_request //在ngx_http_upstream_finalize_request中执行 ngx_http_fastcgi_finalize_request
void (*finalize_request)(ngx_http_request_t *r,
ngx_int_t rc); //请求结束时会调用 //在Nginx完成从上游服务器读入回复以后被调用
/*
在重定向URL阶段,如果实现了rewrite_redirect回调方法,那么这时会调用rewrite_redirect。
可以查看upstream模块的ngx_http_upstream_rewrite_location方法。如果upstream模块接收到完整的上游响应头部,
而且由HTTP模块的process_header回调方法将解析出的对应于Location的头部设置到了ngx_http_upstream_t中的headers in成员时,
ngx_http_upstream_process_headers方法将会最终调用rewrite_redirect方法
因此,rewrite_ redirect的使用场景比较少,它主要应用于HTTP反向代理模蛱(ngx_http_proxy_module)。 赋值为ngx_http_proxy_rewrite_redirect
*/
//在上游返回的响应出现Location或者Refresh头部表示重定向时,会通迂ngx_http_upstream_process_headers方法调用到可由HTTP模块实现的rewrite redirect方法
ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);//ngx_http_upstream_rewrite_location中执行
ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r,
ngx_table_elt_t *h); ngx_msec_t timeout;
//用于表示上游响应的错误码、包体长度等信息
ngx_http_upstream_state_t *state; //从r->upstream_states分配获取,见ngx_http_upstream_init_request //当使用cache的时候,ngx_http_upstream_cache中设置
ngx_str_t method; //不使用文件缓存时没有意义 //GET,HEAD,POST
//schema和uri成员仅在记录日志时会用到,除此以外没有意义
ngx_str_t schema; //就是前面的http,https,mecached:// fastcgt://(ngx_http_fastcgi_handler)等。
ngx_str_t uri; #if (NGX_HTTP_SSL)
ngx_str_t ssl_name;
#endif
//目前它仅用于表示是否需要清理资源,相当于一个标志位,实际不会调用到它所指向的方法
ngx_http_cleanup_pt *cleanup;
//是否指定文件缓存路径的标志位
//xxx_store(例如scgi_store) on | off |path 只要不是off,store都为1,赋值见ngx_http_fastcgi_store
//制定了存储前端文件的路径,参数on指定了将使用root和alias指令相同的路径,off禁止存储,此外,参数中可以使用变量使路径名更明确:fastcgi_store /data/www$original_uri;
unsigned store:1; //ngx_http_upstream_init_request赋值
//后端应答数据在ngx_http_upstream_process_request->ngx_http_file_cache_update中进行缓存
//ngx_http_test_predicates用于可以检测xxx_no_cache,从而决定是否需要缓存后端数据 /*如果Cache-Control参数值为no-cache、no-store、private中任意一个时,则不缓存...不缓存... 后端携带有"x_accel_expires:0"头 参考http://blog.csdn.net/clh604/article/details/9064641
部行也可能置0,参考ngx_http_upstream_process_accel_expires,不过可以通过fastcgi_ignore_headers忽略这些头部,从而可以继续缓存*/
//此外,如果没有使用fastcgi_cache_valid proxy_cache_valid 设置生效时间,则默认会把cacheable置0,见ngx_http_upstream_send_response
unsigned cacheable:1; //是否启用文件缓存 参考http://blog.csdn.net/clh604/article/details/9064641
unsigned accel:1;
unsigned ssl:1; //是否基于SSL协议访问上游服务器
#if (NGX_HTTP_CACHE)
unsigned cache_status:3; //NGX_HTTP_CACHE_BYPASS 等
#endif /*
upstream有3种处理上游响应包体的方式,但HTTP模块如何告诉upstream使用哪一种方式处理上游的响应包体呢?
当请求的ngx_http_request_t结构体中subrequest_in_memory标志位为1时,将采用第1种方式,即upstream不转发响应包体到下游,由HTTP模
块实现的input_filter方法处理包体;
当subrequest_in_memory为0时,upstream会转发响应包体。
当ngx_http_upstream_conf t配置结构体中的buffering标志位为1时,将开启更多的内存和磁盘文件用于缓存上游的响应包体,这意味上游网速更快;
会先buffer后端FCGI发过来的数据,等达到一定量(比如buffer满)再传送给最终客户端
当buffering为0时,将使用固定大小的缓冲区(就是上面介绍的buffer缓冲区)来转发响应包体。 在向客户端转发上游服务器的包体时才有用。
当buffering为1时,表示使用多个缓冲区以及磁盘文件来转发上游的响应包体。
当Nginx与上游间的网速远大于Nginx与下游客户端间的网速时,让Nginx开辟更多的内存甚至使用磁盘文件来缓存上游的响应包体,
这是有意义的,它可以减轻上游服务器的并发压力。
当buffering为0时,表示只使用上面的这一个buffer缓冲区来向下游转发响应包体 从上游接收多少就向下游发送多少,不缓存,这样上游发送速率与下游速率相等
*/ //fastcgi赋值见ngx_http_fastcgi_handler u->buffering = flcf->upstream.buffering; //见xxx_buffering如fastcgi_buffering 是否缓存后端服务器应答回来的包体
//该参数也可以通过后端返回的头部字段: X-Accel-Buffering:no | yes来设置是否开启,见ngx_http_upstream_process_buffering
/*
如果开启缓冲,那么Nginx将尽可能多地读取后端服务器的响应数据,等达到一定量(比如buffer满)再传送给最终客户端。如果关闭,
那么Nginx对数据的中转就是一个同步的过程,即从后端服务器接收到响应数据就立即将其发送给客户端。 buffering标志位为1时,将开启更多的内存和磁盘文件用于缓存上游的响应包体,这意味上游网速更快;当buffering
为0时,将使用固定大小的缓冲区(就是上面介绍的buffer缓冲区)来转发响应包体。
*/ //buffering方式和非buffering方式在函数ngx_http_upstream_send_response分叉
//见xxx_buffering如fastcgi_buffering proxy_buffering 是否缓存后端服务器应答回来的包体
unsigned buffering:1; //向下游转发上游的响应包体时,是否开启更大的内存及临时磁盘文件用于缓存来不及发送到下游的响应
//为1说明本次和后端的连接使用的是缓存cache(keepalive配置)connection的TCP连接,也就是使用的是之前已经和后端建立好的TCP连接ngx_connection_t
//在缓存和后端的连接的时候使用(也就是是否配置了keepalive con-num配置项),为1表示使用的是缓存的TCP连接,为0表示新建的和后端的TCP连接,见ngx_http_upstream_free_keepalive_peer
//此外,在后端服务器交互包体后,如果头部行指定没有包体,则会u->keepalive = !u->headers_in.connection_close;例如ngx_http_proxy_process_header
unsigned keepalive:1;//只有在开启keepalive con-num才有效,释放后端tcp连接判断在ngx_http_upstream_free_keepalive_peer
unsigned upgrade:1; //后端返回//HTTP/1.1 101的时候置1 /*
request_sent表示是否已经向上游服务器发送了请求,当request_sent为1时,表示upstream机制已经向上游服务器发送了全部或者部分的请求。
事实上,这个标志位更多的是为了使用ngx_output_chain方法发送请求,因为该方法发送请求时会自动把未发送完的request_bufs链表记录下来,
为了防止反复发送重复请求,必须有request_sent标志位记录是否调用过ngx_output_chain方法
*/
unsigned request_sent:1; //ngx_http_upstream_send_request_body中发送请求包体到后端的时候置1
/*
将上游服务器的响应划分为包头和包尾,如果把响应直接转发给客户端,header_sent标志位表示包头是否发送,header_sent为1时表示已经把
包头转发给客户端了。如果不转发响应到客户端,则header_sent没有意义
*/
unsigned header_sent:1; //表示头部已经扔给协议栈了,
};
------------------------------------------------------------------------------------------------------------
/*ngx_http_upstream_create创建ngx_http_upstream_t,
资源回收用ngx_http_upstream_finalize_request
upstream资源回收在ngx_http_upstream_finalize_request ngx_http_XXX_handler(ngx_http_proxy_handler)中执行*/
ngx_int_t
ngx_http_upstream_create(ngx_http_request_t *r)//创建一个ngx_http_upstream_t结构,放到r->upstream里面去。
{
ngx_http_upstream_t *u; u = r->upstream; if (u && u->cleanup) {
r->main->count++;
ngx_http_upstream_cleanup(r);
} u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
if (u == NULL) {
return NGX_ERROR;
} r->upstream = u; u->peer.log = r->connection->log;
u->peer.log_error = NGX_ERROR_ERR; u->headers_in.content_length_n = -1;
u->headers_in.last_modified_time = -1; return NGX_OK;
}

 upstream的初始化

/*
1)调用ngx_http_up stream_init方法启动upstream。
2) upstream模块会去检查文件缓存,如果缓存中已经有合适的响应包,则会直接返回缓存(当然必须是在使用反向代理文件缓存的前提下)。
为了让读者方便地理解upstream机制,本章将不再提及文件缓存。
3)回调xxxx模块已经实现的create_request回调方法。
4) xxxx模块通过设置r->upstream->request_bufs已经决定好发送什么样的请求到上游服务器。
5) upstream模块将会检查resolved成员,如果有resolved成员的话,就根据它设置好上游服务器的地址r->upstream->peer成员。
6)用无阻塞的TCP套接字建立连接。
7)无论连接是否建立成功,负责建立连接的connect方法都会立刻返回。
*/
//ngx_http_upstream_init方法将会根据ngx_http_upstream_conf_t中的成员初始化upstream,同时会开始连接上游服务器,以此展开整个upstream处理流程
void ngx_http_upstream_init(ngx_http_request_t *r)
//在读取完浏览器发送来的请求头部字段后,会通过proxy fastcgi等模块读取包体,读取完后执行该函数,例如ngx_http_read_client_request_body(r, ngx_http_upstream_init);
{
ngx_connection_t *c; c = r->connection;//得到客户端连接结构。 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http init upstream, client timer: %d", c->read->timer_set); #if (NGX_HTTP_V2)
if (r->stream) {//http 2.0 初始化
ngx_http_upstream_init_request(r);
return;
}
#endif /*
首先检查请求对应于客户端的连接,这个连接上的读事件如果在定时器中,也就是说,读事件的timer_ set标志位为1,那么调用ngx_del_timer
方法把这个读事件从定时器中移除。为什么要做这件事呢?因为一旦启动upstream机制,就不应该对客户端的读操作带有超时时间的处理(超时会关闭客户端连接),
请求的主要触发事件将以与上游服务器的连接为主。
*/
if (c->read->timer_set) {
ngx_del_timer(c->read, NGX_FUNC_LINE);
} if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { //如果epoll使用边缘触发 //这里实际上是触发执行ngx_http_upstream_check_broken_connection
if (!c->write->active) {//要增加可写事件通知,为啥? //但实际上是检查和客户端的连接是否异常了 if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)
== NGX_ERROR)
{
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
} ngx_http_upstream_init_request(r);
}

ngx_http_upstream_init_request具体实现

//ngx_http_upstream_init调用这里,此时客户端发送的数据都已经接收完毕了。
/*
1. 调用create_request创建fcgi或者proxy的数据结构。
2. 调用ngx_http_upstream_connect连接下游服务器。
*/
static void
ngx_http_upstream_init_request(ngx_http_request_t *r)
{
ngx_str_t *host;
ngx_uint_t i;
ngx_resolver_ctx_t *ctx, temp;
ngx_http_cleanup_t *cln;
ngx_http_upstream_t *u;
ngx_http_core_loc_conf_t *clcf;
ngx_http_upstream_srv_conf_t *uscf, **uscfp;
ngx_http_upstream_main_conf_t *umcf; if (r->aio) {
return;
} u = r->upstream;//ngx_http_upstream_create里面设置的 ngx_http_XXX_handler(ngx_http_proxy_handler)中执行 u->store = u->conf->store; /*
设置Nginx与下游客户端之间TCP连接的检查方法
实际上,这两个方法都会通过ngx_http_upstream_check_broken_connection方法检查Nginx与下游的连接是否正常,如果出现错误,就会立即终止连接。
*/
if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
//注意这时候的r还是客户端的连接,与上游服务器的连接r还没有建立-----也就是在链接上游服务器时 会检查和客户端的链接是否正常
r->read_event_handler = ngx_http_upstream_rd_check_broken_connection; //设置回调需要检测连接是否有问题。
r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
} //有接收到客户端包体,则把包体结构赋值给u->request_bufs,在后面的if (u->create_request(r) != NGX_OK) {会用到
if (r->request_body) {//客户端发送过来的POST数据存放在此,ngx_http_read_client_request_body放的
u->request_bufs = r->request_body->bufs; //记录客户端发送的数据,下面在create_request的时候拷贝到发送缓冲链接表里面的。
//----- r->request_body copyto-----u->request_bufs copy 客户端的数据到上游发送缓存中
} /*
调用请求中ngx_http_upstream_t结构体里由某个HTTP模块实现的create_request方法,构造发往上游服务器的请求
(请求中的内容是设置到request_bufs缓冲区链表中的)。如果create_request方法没有返回NGX_OK,则upstream结束 如果是FCGI。下面组建好FCGI的各种头部,包括请求开始头,请求参数头,请求STDIN头。存放在u->request_bufs链接表里面。
如果是Proxy模块,ngx_http_proxy_create_request组件反向代理的头部啥的,放到u->request_bufs里面
FastCGI memcached uwsgi scgi proxy都会用到upstream模块
*/
if (u->create_request(r) != NGX_OK) { //ngx_http_XXX_create_request ngx_http_proxy_create_request 等
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} /* 获取ngx_http_upstream_t结构中主动连接结构peer的local本地地址信息 */
u->peer.local = ngx_http_upstream_get_local(r, u->conf->local); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); /* 初始化ngx_http_upstream_t结构中成员output向下游发送响应的方式 */
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; if (u->output.output_filter == NULL) {
//设置过滤模块的开始过滤函数为writer。也就是output_filter。在ngx_output_chain被调用已进行数据的过滤
u->output.output_filter = ngx_chain_writer;
u->output.filter_ctx = &u->writer; //参考ngx_chain_writer,里面会将输出buf一个个连接到这里。
} u->writer.pool = r->pool; /* 添加用于表示上游响应的状态,例如:错误编码、包体长度等 */
if (r->upstream_states == NULL) {//数组upstream_states,保留upstream的状态信息。 r->upstream_states = ngx_array_create(r->pool, 1,
sizeof(ngx_http_upstream_state_t));
if (r->upstream_states == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} } else { u->state = ngx_array_push(r->upstream_states);
if (u->state == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
} cln = ngx_http_cleanup_add(r, 0);//环形链表,申请一个新的元素。
if (cln == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} cln->handler = ngx_http_upstream_cleanup; //当请求结束时,一定会调用ngx_http_upstream_cleanup方法
cln->data = r;//指向所指的请求结构体。
u->cleanup = &cln->handler; /*
http://www.pagefault.info/?p=251
然后就是这个函数最核心的处理部分,那就是根据upstream的类型来进行不同的操作,这里的upstream就是我们通过XXX_pass传递进来的值,
这里的upstream有可能下面几种情况。
Ngx_http_fastcgi_module.c (src\http\modules): { ngx_string("fastcgi_pass"),
Ngx_http_memcached_module.c (src\http\modules): { ngx_string("memcached_pass"),
Ngx_http_proxy_module.c (src\http\modules): { ngx_string("proxy_pass"),
Ngx_http_scgi_module.c (src\http\modules): { ngx_string("scgi_pass"),
Ngx_http_uwsgi_module.c (src\http\modules): { ngx_string("uwsgi_pass"),
Ngx_stream_proxy_module.c (src\stream): { ngx_string("proxy_pass"),
1 XXX_pass中不包含变量。
2 XXX_pass传递的值包含了一个变量($开始).这种情况也就是说upstream的url是动态变化的,因此需要每次都解析一遍.
而第二种情况又分为2种,一种是在进入upstream之前,也就是 upstream模块的handler之中已经被resolve的地址(请看ngx_http_XXX_eval函数),
一种是没有被resolve,此时就需要upstream模块来进行resolve。接下来的代码就是处理这部分的东西。
*/
if (u->resolved == NULL) {//上游的IP地址是否被解析过,ngx_http_fastcgi_handler调用ngx_http_fastcgi_eval会解析。 为NULL说明没有解析过,也就是fastcgi_pas xxx中的xxx参数没有变量
uscf = u->conf->upstream; //upstream赋值在ngx_http_fastcgi_pass
} else { //fastcgi_pass xxx的xxx中有变量,说明后端服务器是会根据请求动态变化的,参考ngx_http_fastcgi_handler #if (NGX_HTTP_SSL)
u->ssl_name = u->resolved->host;
#endif
//ngx_http_fastcgi_handler 会调用 ngx_http_fastcgi_eval函数,进行fastcgi_pass 后面的URL的简析,解析出unix域,或者socket.
// 如果已经是ip地址格式了,就不需要再进行解析
if (u->resolved->sockaddr) {//如果地址已经被resolve过了,我IP地址,此时创建round robin peer就行 if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)
!= NGX_OK)
{
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} ngx_http_upstream_connect(r, u);//连接 return;
} //下面开始查找域名,因为fcgi_pass后面不是ip:port,而是url;
host = &u->resolved->host;//获取host信息。
// 接下来就要开始查找域名 umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); uscfp = umcf->upstreams.elts; for (i = 0; i < umcf->upstreams.nelts; i++) {//遍历所有的上游模块,根据其host进行查找,找到host,port相同的。 uscf = uscfp[i];//找一个IP一样的上流模块 if (uscf->host.len == host->len
&& ((uscf->port == 0 && u->resolved->no_port)
|| uscf->port == u->resolved->port)
&& ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0)
{
goto found;//这个host正好相等
}
} if (u->resolved->port == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"no port in upstream \"%V\"", host);
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} //没办法了,url不在upstreams数组里面,也就是不是我们配置的,那么初始化域名解析器
temp.name = *host; // 初始化域名解析器
ctx = ngx_resolve_start(clcf->resolver, &temp);//进行域名解析,带缓存的。申请相关的结构,返回上下文地址。
if (ctx == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} if (ctx == NGX_NO_RESOLVER) {//无法进行域名解析。
// 返回NGX_NO_RESOLVER表示无法进行域名解析
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"no resolver defined to resolve %V", host); ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
return;
} // 设置需要解析的域名的类型与信息
ctx->name = *host;
ctx->handler = ngx_http_upstream_resolve_handler;//设置域名解析完成后的回调函数。
ctx->data = r;
ctx->timeout = clcf->resolver_timeout; u->resolved->ctx = ctx; //开始域名解析,没有完成也会返回的。
if (ngx_resolve_name(ctx) != NGX_OK) {
u->resolved->ctx = NULL;
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} return;
// 域名还没有解析完成,则直接返回
} found: if (uscf == NULL) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"no upstream configuration");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} #if (NGX_HTTP_SSL)
u->ssl_name = uscf->host;
#endif if (uscf->peer.init(r, uscf) != NGX_OK) {//ngx_http_upstream_init_round_XX_peer(ngx_http_upstream_init_round_robin_peer)
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} u->peer.start_time = ngx_current_msec; if (u->conf->next_upstream_tries
&& u->peer.tries > u->conf->next_upstream_tries)
{
u->peer.tries = u->conf->next_upstream_tries;
} ngx_http_upstream_connect(r, u);//调用ngx_http_upstream_connect方法向上游服务器发起连接
}

上游服务器连接ngx_http_upstream_connect处理

/*
upstream机制 与 上游服务器是通过TCP建立连接的,众所周知,建立TCP连接需要三次握手,而三次握手消耗的时间是不可控的。为了保证建立TCP
连接这个操作不会阻塞进程,Nginx使用无阻塞的套接字来连接上游服务器。调用的ngx_http_upstream_connect方法就是用来连接上游服务器的,
由于使用了非阻塞的套接字,当方法返回时与上游之间的TCP连接未必会成功建立,可能还需要等待上游服务器返回TCP的SYN/ACK包。因此,
ngx_http_upstream_connect方法主要负责发起建立连接这个动作,如果这个方法没有立刻返回成功,那么需要在epoll中监控这个套接字,当
它出现可写事件时,就说明连接已经建立成功了。 //调用socket,connect连接一个后端的peer, 然后设置读写事件回调函数,进入发送数据的ngx_http_upstream_send_request里面
//这里负责连接后端服务,然后设置各个读写事件回调。最后如果连接建立成功,会调用ngx_http_upstream_send_request进行数据发送。
*/
static void
ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_int_t rc;
ngx_connection_t *c; r->connection->log->action = "connecting to upstream"; if (u->state && u->state->response_time) {
u->state->response_time = ngx_current_msec - u->state->response_time;
} u->state = ngx_array_push(r->upstream_states);
if (u->state == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t)); u->state->response_time = ngx_current_msec;
u->state->connect_time = (ngx_msec_t) -1;
u->state->header_time = (ngx_msec_t) -1;
//初始赋值见ngx_http_upstream_connect->ngx_event_connect_peer(&u->peer);
//可以看出有多少个客户端连接,nginx就要与php服务器建立多少个连接,为什么nginx和php服务器不只建立一个连接呢????????????????
rc = ngx_event_connect_peer(&u->peer); //建立一个TCP套接字,同时,这个套接字需要设置为非阻塞模式。 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream connect: %i", rc); if (rc == NGX_ERROR) {//
//若 rc = NGX_ERROR,表示发起连接失败,则调用ngx_http_upstream_finalize_request 方法关闭连接请求,并 return 从当前函数返回;
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} u->state->peer = u->peer.name; if (rc == NGX_BUSY) {
//若 rc = NGX_BUSY,表示当前上游服务器处于不活跃状态,则调用 ngx_http_upstream_next 方法根据传入的参数尝试重新发起连接请求,并 return 从当前函数返回;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);
return;
} if (rc == NGX_DECLINED) {
//若 rc = NGX_DECLINED,表示当前上游服务器负载过重,则调用 ngx_http_upstream_next 方法尝试与其他上游服务器建立连接,并 return 从当前函数返回;
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
return;
} /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */ c = u->peer.connection; c->data = r;
/*
设置上游连接 ngx_connection_t 结构体的读事件、写事件的回调方法 handler 都为 ngx_http_upstream_handler,设置 ngx_http_upstream_t
结构体的写事件 write_event_handler 的回调为 ngx_http_upstream_send_request_handler,读事件 read_event_handler 的回调方法为
ngx_http_upstream_process_header;
*/
c->write->handler = ngx_http_upstream_handler;
c->read->handler = ngx_http_upstream_handler; //这一步骤实际上决定了向上游服务器发送请求的方法是ngx_http_upstream_send_request_handler.
//由写事件(写数据或者客户端连接返回成功)触发c->write->handler = ngx_http_upstream_handler;然后在ngx_http_upstream_handler中执行ngx_http_upstream_send_request_handler
u->write_event_handler = ngx_http_upstream_send_request_handler; //如果ngx_event_connect_peer返回NGX_AGAIN也通过该函数触发连接成功
//设置upstream机制的read_event_handler方法为ngx_http_upstream_process_header,也就是由ngx_http_upstream_process_header方法接收上游服务器的响应。
u->read_event_handler = ngx_http_upstream_process_header; c->sendfile &= r->connection->sendfile;
u->output.sendfile = c->sendfile; if (c->pool == NULL) { /* we need separate pool here to be able to cache SSL connections */ c->pool = ngx_create_pool(128, r->connection->log);
if (c->pool == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
} c->log = r->connection->log;
c->pool->log = c->log;
c->read->log = c->log;
c->write->log = c->log; /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts
向上游发送数据时 会使用此结构体,包含简单必要的信息比如:connect out链表
*/
u->writer.out = NULL;
u->writer.last = &u->writer.out;
u->writer.connection = c;
u->writer.limit = 0; if (u->request_sent) {
if (ngx_http_upstream_reinit(r, u) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
} if (r->request_body
&& r->request_body->buf
&& r->request_body->temp_file
&& r == r->main)
//客户端包体存入了临时文件后,则使用r->request_body->bufs链表中的ngx_buf_t结构的file_pos和file_last指向,所以r->request_body->buf可以继续读取包体
{
/*
* the r->request_body->buf can be reused for one request only,
* the subrequests should allocate their own temporary bufs
*/ u->output.free = ngx_alloc_chain_link(r->pool);
if (u->output.free == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
} u->output.free->buf = r->request_body->buf;
u->output.free->next = NULL;
u->output.allocated = 1; r->request_body->buf->pos = r->request_body->buf->start;
r->request_body->buf->last = r->request_body->buf->start;
r->request_body->buf->tag = u->output.tag;
} u->request_sent = 0; if (rc == NGX_AGAIN) { //这里的定时器在ngx_http_upstream_send_request中删除,已经建立链接数据交互,
/*
2025/04/24 02:54:29[ ngx_epoll_process_events, 1772] [debug] 14867#14867: *1 post event AEB44098
2025/04/24 02:54:29[ ngx_process_events_and_timers, 371] [debug] 14867#14867: epoll_wait timer range(delta): 2
2025/04/24 02:54:29[ ngx_event_process_posted, 65] [debug] 14867#14867: posted event AEB44068
2025/04/24 02:54:29[ ngx_event_process_posted, 67] [debug] 14867#14867: *1 delete posted event AEB44068
2025/04/24 02:54:29[ ngx_http_request_handler, 2400] [debug] 14867#14867: *1 http run request: "/test.php?"
2025/04/24 02:54:29[ngx_http_upstream_check_broken_connection, 1335] [debug] 14867#14867: *1 http upstream check client, write event:1, "/test.php"
2025/04/24 02:54:29[ngx_http_upstream_check_broken_connection, 1458] [debug] 14867#14867: *1 http upstream recv(): -1 (11: Resource temporarily unavailable)
2025/04/24 02:54:29[ ngx_event_process_posted, 65] [debug] 14867#14867: posted event AEB44098
2025/04/24 02:54:29[ ngx_event_process_posted, 67] [debug] 14867#14867: *1 delete posted event AEB44098
2025/04/24 02:54:29[ ngx_http_upstream_handler, 1295] [debug] 14867#14867: *1 http upstream request: "/test.php?"
2025/04/24 02:54:29[ngx_http_upstream_send_request_handler, 2210] [debug] 14867#14867: *1 http upstream send request handler
2025/04/24 02:54:29[ ngx_http_upstream_send_request, 2007] [debug] 14867#14867: *1 http upstream send request
2025/04/24 02:54:29[ngx_http_upstream_send_request_body, 2095] [debug] 14867#14867: *1 http upstream send request body
2025/04/24 02:54:29[ ngx_chain_writer, 690] [debug] 14867#14867: *1 chain writer buf fl:0 s:968
2025/04/24 02:54:29[ ngx_chain_writer, 704] [debug] 14867#14867: *1 chain writer in: 080EC838
2025/04/24 02:54:29[ ngx_writev, 192] [debug] 14867#14867: *1 writev: 968 of 968
2025/04/24 02:54:29[ ngx_chain_writer, 740] [debug] 14867#14867: *1 chain writer out: 00000000
2025/04/24 02:54:29[ ngx_event_del_timer, 39] [debug] 14867#14867: *1 <ngx_http_upstream_send_request, 2052> event timer del: 12: 1677807811//这里删除
2025/04/24 02:54:29[ ngx_event_add_timer, 88] [debug] 14867#14867: *1 <ngx_http_upstream_send_request, 2075> event timer add: 12: 60000:1677807813
*/
/*
若 rc = NGX_AGAIN,表示当前已经发起连接,但是没有收到上游服务器的确认应答报文,即上游连接的写事件不可写,则需调用 ngx_add_timer
方法将上游连接的写事件添加到定时器中,管理超时确认应答; 这一步处理非阻塞的连接尚未成功建立时的动作。实际上,在ngx_event_connect_peer中,套接字已经加入到epoll中监控了,因此,
这一步将调用ngx_add_timer方法把写事件添加到定时器中,超时时间为ngx_http_upstream_conf_t结构体中的connect_timeout
成员,这是在设置建立TCP连接的超时时间。
*/ //这里的定时器在ngx_http_upstream_send_request会删除
ngx_add_timer(c->write, u->conf->connect_timeout, NGX_FUNC_LINE);
return; //大部分情况通过这里返回,然后通过ngx_http_upstream_send_request_handler来执行epoll write事件
} //若 rc = NGX_OK,表示成功建立连接,则调用 ngx_http_upsream_send_request 方法向上游服务器发送请求;
#if (NGX_HTTP_SSL) if (u->ssl && c->ssl == NULL) {
ngx_http_upstream_ssl_init_connection(r, u, c);
return;
} #endif //如呆已经成功建立连接,则调用ngx_http_upstream_send_request方法向上游服务器发送请求
ngx_http_upstream_send_request(r, u, 1);
}

http 代理阅读1的更多相关文章

  1. http代理阅读4 响应缓存处理

    if (c->read->ready) { ngx_http_upstream_process_header(r, u); //读事件触发 准备处理http头部信息 return; } 向 ...

  2. http代理阅读3 发送mem处理

    每次客户端有可读数据触发时,优先检测是否还有数据没有发送,如果有则发送数据,然后在读取client数据 //向后端发送请求的调用过程 //ngx_http_upstream_send_request_ ...

  3. http代理阅读2

    向上游服务器发送请求处理 static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t * ...

  4. HTTP协议 (五) 代理

    HTTP协议 (五) 代理 阅读目录 什么是代理服务器 Fiddler就是个典型的代理 代理作用一:FQ 代理作用二:匿名访问 代理作用三:通过代理上网 代理作用四:通过代理缓存,加快上网速度 代理作 ...

  5. iOS各种问题处理

    本文转载至:http://www.cnblogs.com/ygm900/category/436923.html 推荐初学者前去学习.     mac 拷贝文件时报错 8060 解决方案 摘要: 解决 ...

  6. Hadoop阅读笔记(七)——代理模式

    关于Hadoop已经小记了六篇,<Hadoop实战>也已经翻完7章.仔细想想,这么好的一个框架,不能只是流于应用层面,跑跑数据排序.单表链接等,想得其精髓,还需深入内部. 按照<Ha ...

  7. 通过爬虫代理IP快速增加博客阅读量——亲测CSDN有效!

    写在前面 题目所说的并不是目的,主要是为了更详细的了解网站的反爬机制,如果真的想要提高博客的阅读量,优质的内容必不可少. 了解网站的反爬机制 一般网站从以下几个方面反爬虫: 1. 通过Headers反 ...

  8. mybatis源码阅读(动态代理)

    这一篇文章主要是记录Mybatis的动态代理学习成果,如果对源码感兴趣,可以看一下上篇文章  https://www.cnblogs.com/ChoviWu/p/10118051.html 阅读本篇的 ...

  9. javascript设计模式与开发实践阅读笔记(6)——代理模式

    代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问. 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对 ...

随机推荐

  1. Avoid mutating a prop directly since the value will be overwritten whenever the parent component re

    子组件修改父组件的值踩坑 Vue1.0升级至2.0之后,直接在子组件修改父组件的值是会报错的 目的是为了阻止子组件影响父组件的数据. 我们都知道在vue中,父组件传入子组件的变量是存放在props属性 ...

  2. hdfs的JAVA必会操作

    hdfs的必会操作 创建目录 //创建目录 public static void mkdir(String filePath) throws URISyntaxException, IOExcepti ...

  3. Android开发教程之密码框右侧显示小眼睛

    现在都说互联网寒冬,其实只要自身技术能力够强,咱们就不怕!我这边专门针对Android开发工程师整理了一套[Android进阶学习视频].[全套Android面试秘籍].[Android知识点PDF] ...

  4. day51 Pyhton 前端02

    内容回顾: 1.h1~h6:加粗,数字越大级别越小,自动换行 2.br:换行;hr:分割线; (特殊符号,空格) 3.p:与前边和后边内容之间有间距 4.a标签的href:本地文件连接;网络连接;锚链 ...

  5. spring boot:用spring security加强spring boot admin的安全(spring boot admin 2.3.0 / spring boot 2.3.3)

    一,spring boot admin的安全环节: 1,修改context-path,默认时首页就是admin, 我们修改这个地址可以更安全 2,配置ip地址白名单,有ip限制才安全, 我们使用了sp ...

  6. seajs模块化jQuery与jQuery插件【转】

    把jQuery修改成SeaJs的模块代码非常简单,就是用下面这段语句将jQuery源代码包裹起来: define('jquery',[],function(require, exports, modu ...

  7. 【抽五分钟】使用VuePress创建在线文档中心

    内容目录 安装初始化核心配置导航栏配置侧边栏配置静态资源配置nginx部署typora编写 安装初始化 全局安装  npm install -g vuepress 创建目录 mkdir vurepre ...

  8. 全宇宙首个.NET5+Vue.js前后端分离以及业务模块化快速开发框架【NetModular】发布~

    最近.Net圈子很热闹啊,我也来凑凑,今天中午耗时长达半小时,把NetModular升级到了.NET5,详情查看分支https://github.com/iamoldli/NetModular/tre ...

  9. [C#.NET 拾遗补漏]10:理解 volatile 关键字

    要理解 C# 中的 volatile 关键字,就要先知道编译器背后的一个基本优化原理.比如对于下面这段代码: public class Example { public int x; public v ...

  10. 第 1 篇:Vue.js 很高兴认识你

    作者:HelloGitHub--追梦人物 Hello Vue 既然是学习编程,那就遵循一下那个古老的传统仪式. 首先我们新建一个 todos.html 文件,用任何一个你喜欢的文本编辑器或者 IDE ...