Nginx源代码分析之upstream(八)

由于Nginx只能处理静态页面,对于大部分网站来说想利用的他的高性能,则把NGINX作为一个前端的负载均衡服务器,主要作用是对HTTP进行反向代理。

而反向代理是由upstaream部分来实现的。


在ngx_http_proxy_module.c中,ngx_http_proxy_handler会注册ngx_http_upstream_init作为处理方向代理的处理函数。

首先是调用ngx_http_read_client_request_body,读取来自客户的HTTP请求部分,其中有很多细节,但千回婉转,目的只有一个,复制客户端的数据,并作为ngx_http_upstream_init的body部分再发送出去。


看看ngx_http_upstream_init,主要就是调用ngx_http_upstream_init_request


此函数主要执行以下任务

一,创建HTTP请求,生成http head和body:

    if (r->request_body) {
        u->request_bufs = r->request_body->bufs;
    }


    if (u->create_request(r) != NGX_OK) {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }


二注册一系列写数据的过滤和回调函数

    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;


其中比较重要的是ngx_chain_writer,稍后就会看到。

最后调用ngx_http_upstream_connect来建立TCP链接,ngx_http_upstream_connect在前面的文章中已经提到过。


connect建立连接成功的话,会向后端服务器发送HTTP GET或者是POST,

如果是HTTPS,会调用ngx_http_upstream_ssl_init_connection,如果是普通HTTP,会调用ngx_http_upstream_send_request。


来看看ngx_http_upstream_send_request的实现,其作用就是组织HTTP头部和数据段,并通过套接字发送出去。

关键点在于ngx_output_chain,如果gx_output_chain调用失败,会接着调用ngx_http_upstream_next,准备下一个请求的发送。

如果处于等待状态,则会调用ngx_add_timer添加一个超时器,以免该请求无限等待下去。然后向最底层的I/O模型注册一个写的异步调用,代码如下:

    if (rc == NGX_AGAIN) {
        ngx_add_timer(c->write, u->conf->send_timeout);


        if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {
            ngx_http_upstream_finalize_request(r, u,
                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }


        return;
    }


如果gx_output_chain成功,说明tcp的write或者writev直接发送成功了,首先会强制设置TCP的push标志。由于在ngx_http_upstream_connect函数里面已经注册了读的异步回调ngx_http_upstream_process_header,因此最后会坚持c->read->ready标志,如果为真,说明已经收到HTTP返回的数据,这里可以直接调用ngx_http_upstream_process_header以处理返回的HTTP报文。


在回头看看ngx_output_chain的详细实现

在调用ngx_output_chain的时候,传递2个参数,一个是u->outpu是发送缓冲的上下文,u->request_bufs是数据缓存,request_sent 为0说明还没有调用写函数发送过数据。如果没调用过,那就需要把u->request_bufs的数据发送出去。


在函数开始首先判断发送缓冲中是否有未发的数据,如果是空的,就调用ctx->output_filter把当前request_bufs的数据直接发送出去,如果output里面有等待发送的数据,需要把当前需发送的数据拷贝到缓冲里面,目的是经量组成足够大的TCP包一次性发送出去,减少包的数量。

代码是:

    if (in) {
        if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }


接下来主要是一个无限循环,里面又嵌套了一个循环,里面的循环就是遍历in这个链表,把数据都放到一个临时的out链表上面去,然后

last = ctx->output_filter(ctx->filter_ctx, out);

duang的一下发送出去,output_filter是一个函数指针,指向的正是前面提到的ngx_chain_writer。

接着

        ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,
                                ctx->tag);

把out指向的剩余的缓存链放入ctx->busy链表中,如果busy里面的数据都发送完了,则把里面的缓存放到ctx-free里面。


ngx_chain_writer函数的内部,第一个循环



    for (size = 0; in; in = in->next) {


#if 1
        if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {
            ngx_debug_point();
        }
#endif


        size += ngx_buf_size(in->buf);


        ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
                       "chain writer buf fl:%d s:%uO",
                       in->buf->flush, ngx_buf_size(in->buf));


        cl = ngx_alloc_chain_link(ctx->pool);
        if (cl == NULL) {
            return NGX_ERROR;
        }


        cl->buf = in->buf;
        cl->next = NULL;
        *ctx->last = cl;
        ctx->last = &cl->next;
    }


把in指向的链表加入到ngx_chain_writer_ctx_t的out指向的链表中,这是真正的发送缓冲链表。

接着又是一个循环,计算ctx->out指向的全部缓存的总的大小,接着就调用send_chain来发送数据。

send_chain是系统相关的调用,windows是下指向ngx_wsasend_chain.c里面的ngx_wsasend_chain,unix系统则指向ngx_writev_chain.c里面的ngx_writev_chain,其主要内容只是遍历out链表,对于每个nginx_chain_t,调用WSASend或者writev。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值