赞
踩
Nginx作为proxy,其长短连接有2部分,一部分是client一部分是server。
先梳理一下http_proxy的流程
1:downstream 如果 location 配置了 proxy_pass,则会ngx_http_core_content_phase
函数中调用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));//r->content_handler就是ngx_http_proxy_handler
return NGX_OK;
}
2:ngx_http_proxy_handler函数最后调用 了
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
如果没有配置discard body,则会在此处调用ngx_http_upstream_init
函数,然后无条件返回NGX_OK。
if (rb->rest == 0) {
/* the whole request body was pre-read */
r->request_body_no_buffering = 0;
post_handler(r);//post_handler就是ngx_http_upstream_init
return NGX_OK;
}
而 ngx_http_proxy_handler
函数无条件返回了NGX_DONE。
3:ngx_http_upstream_init
执行了 包括寻找 upstream的server,connect server等逻辑,connect server之后,会发送request,然后读取response,然后把response回复给downstream,当然,这一些并不意味着,ngx_http_upstream_init
返回了就表示这一切完成了,因为Nginx是非阻塞的,所以一个简单的connect返回 in_process ngx_http_upstream_init
函数就会返回了。
我们返回流程1,ngx_http_proxy_handler
返回后,其返回值作为ngx_http_finalize_request
的第二个参数传入,2中说了,ngx_http_proxy_handler
函数无条件返回了NGX_DONE。
于是ngx_http_finalize_request
中也无条件调用了
if (rc == NGX_DONE) {
ngx_http_finalize_connection(r);
return;
}
当然,ngx_http_finalize_connection
看到不会无条件的释放connection,上面说过,跑到这里,是因为ngx_http_proxy_handler
返回了,而ngx_http_proxy_handler
返回了则表示ngx_http_upstream_init
返回了,但是ngx_http_upstream_init
返回了并不意味着upstrem完成了操作,我们不能把downstream的connection给释放。
ngx_http_finalize_connection
函数有这么一个逻辑,我们当前 r->main = r
。
但是我们的r->cout == 2
,为何呢?因为在ngx_http_read_client_request_body
中开头,我们将其进行了++
if (r->main->count != 1) {
if (r->discard_body) {
r->read_event_handler = ngx_http_discarded_request_body_handler;
ngx_add_timer(r->connection->read, clcf->lingering_timeout);
if (r->lingering_time == 0) {
r->lingering_time = ngx_time()
+ (time_t) (clcf->lingering_time / 1000);
}
}
ngx_http_close_request(r, 0);//ngx_http_close_request也会不真的释放r和c,而是将count--
return;
}
个人理解,为了在Nginx的content phase拦截住请求,那么ngx_http_proxy_handler
函数必然需要返回NGX_DONE
,而返回了这个值,外层函数ngx_http_core_content_phase
必然会调用ngx_http_finalize_request
去结束r,为了r不被释放,只能在ngx_http_proxy_handler
流程中,对r->count
进行++,这样free时,就不会真的free了。
4:那我们的r什么时候释放呢,我们知道r也会作为upstrem的r,所以当upstream处理完成
调用栈如下
ngx_http_upstream_finalize_request
ngx_http_upstream_process_non_buffered_request
ngx_http_upstream_process_non_buffered_downstream
ngx_http_upstream_send_response
ngx_http_upstream_handler
ngx_http_upstream_finalize_request
有这个if,表示upstream的c存在,先释放了upstream的c
if (u->peer.connection) {
......
}
然后在ngx_http_upstream_finalize_request
最后,调用了ngx_http_finalize_request再次释放了r,第二个参数是NGX_OK。
接着在ngx_http_finalize_request
最后调用了ngx_http_finalize_connection
。因为此时count为1,所以可以顺利的释放r。释放r的时候。
总结一下,就是downstream把request全部读取后,会尝试和upstream连,这里我们不关心怎么连。
然后downstream执行ngx_http_finalize_request
,第二个参数是NGX_DONE,只是简单的将r的计数减1。剩下的事情交给upstream。
upstream接受完server的数据后,发送到downstream,发送完之后,调用ngx_http_upstream_finalize_request
。该函数先释放upststream的c,然后释放r,由于r引用计数为1了,所以也会释放r对于的c,即downstream的c。
注意:upstream在正确接受完数据后才会去主动关闭连接。不正确接受数据,例如content-length和实际数据不匹配,则不会关闭连接,那么这种情况下,除非upstream主动断开连接,否则client不能继续读数据。
ngx_http_finalize_connection
的时候,判断配置是否开启了keepalive以及r->keepalive
是否为1;
HTTP1.1的情况下,且downstream的connection首部是keep-alive,则r->keepalive时1,当异常情况时,会被置为0,即不允许被keepalive。在代理的情况下,被置为0的情况非常小,也即在代理的情况下,只要开启了keepalive,downstrem的连接总是能保持的。
if (!ngx_terminate
&& !ngx_exiting
&& r->keepalive
&& clcf->keepalive_timeout > 0)
{
ngx_http_set_keepalive(r);
return;
}
在upstream尝试关闭upstream连接时,会调用ngx_http_upstream_finalize_request
。
在downstream尝试关闭downstream时,也会去关闭upstream。
如果upstream还在和downstream传输数据,那么此时downstream关闭连接时会触发ngx_http_upstream_rd_check_broken_connection
,其中会调用ngx_http_upstream_finalize_request
。
ngx_http_upstream_finalize_request
if (u->peer.free && u->peer.sockaddr) {
u->peer.free(&u->peer, u->peer.data, 0);
u->peer.sockaddr = NULL;
}
/*如果存在upstream的c则关闭*/
if (u->peer.connection) {
....
ngx_close_connection(u->peer.connection);
}
如果upstream开启了keepalive则,u->peer.free
被赋值成 ngx_http_upstream_free_keepalive_peer
。
其主要目的就是判断这个连接是否有效,若有效,则把upstream的c放到复用池里面,然后把u->peer.connection
置为NULL,免得外层函数对其free。
何为upstream有效?其实约束条件挺多。比如上面说过,如果upstream发送的数据长度和content-length不匹配,则upstream就不会主动调用那个ngx_http_upstream_finalize_request
断开连接,也就意味它只能等待upstream主动发送fin或者rst,这种去情况下,本来就没有能力复用。
如果upstream不主动关闭连接,downstream主动关闭连接,那么,会判断u->keepalive标记是否为1。u->keepalive只会在u->input_filter中被至上。u->input_filter 被 ngx_http_upstream_send_response
调用。主要是统计实际字节数与content-length表示则字节数书否一致。一致才会置 1。所以这也是upstream能够被keepalive的条件之一。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。