Nginx源码分析之 长短连接

Nginx源码分析之 长短连接:

Nginx作为proxy,其长短连接有2部分,一部分是client一部分是server。

downstream 和 upstream 未开keepalive

先梳理一下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不能继续读数据。

downstream开启keepalive

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 开启 keepalive

在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的条件之一。

猜你喜欢

转载自blog.csdn.net/mrpre/article/details/80569627
今日推荐