nginx的upstream模块数据转发过程及流量控制分析

1.基本介绍

关于nginx的upstream模块的基本原理及设计,可参考点击打开链接。其实它就是一个特殊的handler模块,各种XX_proxy指令的set函数,会设置该location的content_handler,如XX_memcached_module的ngx_http_memcached_handler(),XX_proxy_module的ngx_http_proxy_handler()。可以简单地阅读一下这些代码,会发现它们几乎差不多

1. 先创建一个upstream结构,简单的初始化;

2. 挂载一些hook函数,如create_request,reinit_request,process_header,finalize_request等(这种hook的实现方式,很好的统一了各种upstream模块的总体架构,只在具体协议相关的部分区别);

3. 调用ngx_http_upstream_init(r)来初始化上游连接。

其中,ngx_http_upstream_init()函数是一个关键,它会找到一个上游server(依据特定的算法),建立tcp连接,并为downstream、upstream两个连接设置read/write_handler,并加入到epoll中。这里有很多值得学习的地方,如寻找peer.connection的方法,非阻塞的建立tcp连接的方式等。到这里,整个content_handler就结束了,之后的事情完全由事件驱动。


2.工作流程

下面重点看一下之后的工作流程。一个比较大的文件传输,通常会需要多次epoll_wait来完成整个请求,如请求上游服务器中的一张图片(150K),工作过程如下:



其中XX_upstream_process_upstream()是控制数据流转发的关键,它这要是调用ngx_event_pipe(),具体看这个函数

ngx_int_t
ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)
{
    u_int         flags;
    ngx_int_t     rc;
    ngx_event_t  *rev, *wev;

    for ( ;; ) {		//循环处理,从上游到下游转发数据
        if (do_write) {
            p->log->action = "sending to client";

            rc = ngx_event_pipe_write_to_downstream(p);

            if (rc == NGX_ABORT) {
                return NGX_ABORT;
            }

		//向downstream发不动了,那就也不用再从upstream读了
            if (rc == NGX_BUSY) {	
                return NGX_OK;
            }
        }

        p->read = 0;
        p->upstream_blocked = 0;

        p->log->action = "reading upstream";

        if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) {
            return NGX_ABORT;
        }

/* 没读到数据,但若是因为p->upstream_blocked,即上游应用层读缓存不够了,那么还可以继续向下游写,从而释放上游的读缓存;但若不是因为这个,则可能是出现了错误、或出错了,则退出 */
        if (!p->read && !p->upstream_blocked) {
            break;
        }

        do_write = 1;
    }

    if (p->upstream->fd != -1) {
	  继续添加读event到epoll中,并添加超时        
    }

    if (p->downstream->fd != -1 && p->downstream->data == p->output_ctx) {
	  同上        
    }

    return NGX_OK;
}
其中ngx_event_pipe_read_upstream(p)主体是一个for(;;)循环,调用ngx_readv_chain(c,chain)把上游数据读到应用层,再通过ngx_http_proxy_copy_filter(),过滤数据,并最终挂在p->in链上。

ngx_event_pipe_write_to_downstream(p)主体也是一个for(;;)循环,整合p->busy,p->out,p->in组成链out,调用ngx_http_top_body_filter(r,out),进入filter链,直到最后的ngx_http_write_filter,把数据发送给client,若来不及发,则把剩余的数据挂在r->out上。


3.数据流量

到这里,upstream基本的工作流程就清楚了。如前所述,可以看到在传输数据的过程中,数据的流量控制往往是导致过程复杂化的原因,并且由于要同时维护两个连接,尤其是后端up-server在一些特殊情况下(如阻塞)的行为很难预知,更要仔细地处理每种情况。下面以数据流量控制为契机,重新看数据的转化过程。



数据流量的控制,涉及到tcp连接的双方,并且与底层协议大有关联,也会有很多问题,以后再不断看把。






展开阅读全文

没有更多推荐了,返回首页