Nginx之upstream机制(一)

1. 数据结构

1.1 ngx_http_upstream_t

typedef struct ngx_http_upstream_s    ngx_http_upstream_t;

struct ngx_http_upstream_s {
    /*
     * 处理读事件的回调方法,每一个阶段都有不同的 read_event_handler 
     */
    ngx_http_upstream_handler_pt     read_event_handler;
    /*
     * 处理写事件的回调方法,每一个阶段都有不同的 write_event_handler
     */
    ngx_http_upstream_handler_pt     write_event_handler;

    /*
     * 表示主动向上游服务器发起的连接
     */
    ngx_peer_connection_t            peer;

    /*
     * 当向下游客户端转发响应时(ngx_http_request_t 结构体中的 subrequest_in_memory
     * 标志位为 0),如果打开了缓存且认为上游网速更快(conf 配置中的 buffering 标志
     * 位为 1),这时会使用 pipe 成员来转发响应。在使用这种方式转发响应时,必须由
     * HTTP 模块在使用 upstream 机制前构造 pipe 结构体,否则会出现严重的 coredump
     * 错误.
     */
    ngx_event_pipe_t                *pipe;

    /*
     * request_bufs 以链表的方式把 ngx_buf_t 缓存区链接起来,它表示所有需要发送到
     * 上游服务器的请求内容。所以,HTTP 模块实现的 create_request 回调方法就在于
     * 构造 request_bufs 链表
     */
    ngx_chain_t                     *request_bufs;

    /*
     * 定义了向下游发送响应的方式
     */
    ngx_output_chain_ctx_t           output;
    ngx_chain_writer_ctx_t           writer;

    /*
     * 使用 upstream 机制时的各种配置
     */
    ngx_http_upstream_conf_t        *conf;
    ngx_http_upstream_srv_conf_t    *upstream;
#if (NGX_HTTP_CACHE)
    ngx_array_t                     *caches;
#endif

    /*
     * HTTP 模块在实现 process_header 方法时,如果希望 upstream 直接转发响应,
     * 就需要把解析出的响应头部适配为 HTTP 的响应头部,同时需要把包头中的信息
     * 设置到 headers_in 结构体,这样,会把 headers_in 中设置的头部添加到要发
     * 送到下游客户端的响应头部 headers_out 中
     */
    ngx_http_upstream_headers_in_t   headers_in;

    /*
     * 用于解析主机域名
     */
    ngx_http_upstream_resolved_t    *resolved;

    ngx_buf_t                        from_client;

    /*
     * 接收上游服务器响应包头的缓冲区,在不需要把响应直接转发给客户端,
     * 或者 buffering 标志位为 0 的情况下转发包体时,接收包体的缓冲区
     * 仍然使用 buffer。注意,如果没有自定义 input_filter 方法处理包体,
     * 将会使用 buffer 存储全部的包体,这时 buffer 必须足够大,它的大小
     * 由 ngx_http_upstream_conf_t 配置结构体中的 buffer_size 成员决定
     */
    ngx_buf_t                        buffer;
    /*
     * 表示来自上游服务器的响应包体的长度
     */
    off_t                            length;

    /*
     * out_bufs 在两种场景下有不同的意义:1. 当不需要转发包体,且使用默认
     * 的 input_filter 方法(也就是 ngx_http_upstream_non_buffered_filter 
     * 方法)处理包体时,out_bufs 将会指向响应包体,事实上,out_bufs 链表
     * 中会产生多个 ngx_buf_t 缓冲区,每个缓冲区都指向 buffer 缓存中的一部
     * 分,而这里的一部分就是每次调用 recv 方法接收到的一段 TCP 流。2. 当
     * 需要转发响应包体到下游时(buffering 标志位为 0,即以下游网速优先),
     * 这个链表指向上一次向下游转发响应到现在这段时间内接收自上游的缓存响应
     */
    ngx_chain_t                     *out_bufs;
    /*
     * 当需要转发响应包体到下游时(buffering 标志位为 0,即以下游网速优先),
     * 它表示上一次向下游转发响应时没有发送完的内容
     */
    ngx_chain_t                     *busy_bufs;
    /*
     * 这个链表将用于回收 out_bufs 中已经发送给下游的 ngx_buf_t 结构体,这
     * 同样应用在 buffering 标志位为 0 即以下游网速优先的场景
     */
    ngx_chain_t                     *free_bufs;

    /*
     * 处理包体前的初始化方法,其中 data 参数用于传递用户数据结构,它实际上
     * 就是下面的 input_filter_ctx 指针
     */
    ngx_int_t                      (*input_filter_init)(void *data);
    /*
     * 处理包体的方法,其中 data 参数用于传递用户数据结构,它实际上就是下面的
     * input_filter_ctx 指针,而 bytes 表示本次接收到的包体长度。返回 NGX_ERROR
     * 时表示处理包体错误,请求需要结束,否则都将继续 upstream 流程
     */
    ngx_int_t                      (*input_filter)(void *data, ssize_t bytes);
    /*
     * 用于传递 HTTP 模块自定义的数据结构,在 input_filter_init 和 input_filter 
     * 方法被回调时会作为参数传递过去
     */
    void                            *input_filter_ctx;

#if (NGX_HTTP_CACHE)
    ngx_int_t                      (*create_key)(ngx_http_request_t *r);
#endif
    /*
     * HTTP 模块实现的 create_request 方法用于构造发往上游服务器的请求
     */
    ngx_int_t                      (*create_request)(ngx_http_request_t *r);
    /*
     * 与上游服务器的通信失败后,如果按照重试规则还需要再次向上游服务器发起
     * 连接,则会调用 reinit_request 方法
     */
    ngx_int_t                      (*reinit_request)(ngx_http_request_t *r);
    /*
     * 解析上游服务器返回响应的包头,返回 NGX_AGAIN 表示包头还没有接收完整,
     * 返回 NGX_HTTP_UPSTREAM_INVALID_HEADER 表示包头不合法,返回 NGX_ERROR
     * 表示出现错误,返回 NGX_OK 表示解析到完整的包头.
     */
    ngx_int_t                      (*process_header)(ngx_http_request_t *r);
    void                           (*abort_request)(ngx_http_request_t *r);
    /*
     * 请求结束时会调用
     */
    void                           (*finalize_request)(ngx_http_request_t *r,
                                         ngx_int_t rc);
    /*
     * 在上游返回的响应出现 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_int_t                      (*rewrite_cookie)(ngx_http_request_t *r,
                                         ngx_table_elt_t *h);

    ngx_msec_t                       timeout;

    /*
     * 用于表示上游响应的错误码、包体长度等信息
     */
    ngx_http_upstream_state_t       *state;

    ngx_str_t                        method;
    /*
     * schema 和 uri 成员仅在记录日志时会用到,除此之外没有意义
     */
    ngx_str_t                        schema;
    ngx_str_t                        uri;

#if (NGX_HTTP_SSL || NGX_COMPAT)
    ngx_str_t                        ssl_name;
#endif

    ngx_http_cleanup_pt             *cleanup;

    /*
     * 是否指定文件缓存路径的标志位
     */
    unsigned                         store:1;
    /*
     * 是否启用文件缓存
     */
    unsigned                         cacheable:1;
    unsigned                         accel:1;
    /*
     * 是否基于 SSL 协议访问上游服务器
     */
    unsigned                         ssl:1;
#if (NGX_HTTP_CACHE)
    unsigned                         cache_status:3;
#endif

    /*
     * 向下游转发上游的响应包体时,是否开启更大的内存及临时磁盘文件用于
     * 缓存来不及发送到下游的响应包体.
     */
    unsigned                         buffering:1;
    unsigned                         keepalive:1;
    unsigned                         upgrade:1;

    /*
     * request_sent 表示是否已经向上游服务器发送了请求,当 request_sent 为 
     * 1 时,表示 upstream 机制已经向上游服务器发送了全部或者部分的请求。
     * 事实上,这个标志位更多的是为了使用 ngx_output_chain 方法发送请求,
     * 因为该方法发送请求时会自动把未发送完的 request_bufs 链表记录下来,
     * 为了防止反复发送重复请求,必须有 request_sent 标志位记录是否调用过
     * ngx_output_chain 方法
     */
    unsigned                         request_sent:1;
    unsigned                         request_body_sent:1;
    /*
     * 将上游服务器的响应划分为包头和包尾,如果把响应直接转发给客户端,
     * header_sent 标志位表示包头是否发送,header_sent 为 1 时表示已经
     * 把包头转发给客户端了。如果不转发响应到客户端,则 header_sent 
     * 没有意义.
     */
    unsigned                         header_sent:1;
};

1.2 ngx_http_upstream_conf_t

typedef struct {
    /*
     * 当在 ngx_http_upstream_t 结构体中没有实现 resolved 成员时,upstream 这个
     * 结构体才会生效,它会定义上游服务器的配置
     */
    ngx_http_upstream_srv_conf_t    *upstream;

    /*
     * 建立 TCP 连接的超时时间,实际上就是写事件添加到定时器中设置的超时时间
     */
    ngx_msec_t                       connect_timeout;
    /*
     * 发送请求的超时时间。通常就是写事件添加到定时器中设置的超时时间
     */
    ngx_msec_t                       send_timeout;
    /*
     * 接收响应的超时时间。通常就是读事件添加到定时器中设置的超时时间
     */
    ngx_msec_t                       read_timeout;
    ngx_msec_t                       next_upstream_timeout;

    /*
     * TCP 的 SO_SNOLOWAT 选项,表示发送缓冲区的下限
     */
    size_t                           send_lowat;
    /*
     * 定义了接收头部的缓冲区分配的内存大小(ngx_http_upstream_t 中的 buffer 
     * 缓冲区),当不转发响应给下游或者在 buffering 标志位为 0 的情况下转发
     * 响应时,它同样表示接收包体的缓冲区大小
     */
    size_t                           buffer_size;
    size_t                           limit_rate;

    /*
     * 仅当 buffering 标志位为 1,并且向下游转发响应时生效。它会设置到 
     * ngx_event_pipe_t 结构体的 busy_size 成员中
     */
    size_t                           busy_buffers_size;
    /*
     * 在 buffering 标志位为 1 时,如果上游速度快于下游速度,将有可能把来自上游的
     * 响应存储到临时文件中,而 max_temp_file_size 指定了临时文件的最大长度。实际
     * 上,它将限制 ngx_event_pipe_t 结构体中的 temp_file 
     */
    size_t                           max_temp_file_size;
    /*
     * 表示将缓冲区中的响应写入临时文件时一次写入字符流的最大长度
     */
    size_t                           temp_file_write_size;

    size_t                           busy_buffers_size_conf;
    size_t                           max_temp_file_size_conf;
    size_t                           temp_file_write_size_conf;

    /*
     * 以缓存响应的方式转发上游服务器的包体时所使用的内存大小
     */
    ngx_bufs_t                       bufs;

    /*
     * 针对 ngx_http_upstream_t 结构体中保存解析完的包头的 headers_in 成员,
     * ignore_headers 可以按照二进制位使得 upstream 在转发包头时跳过对某些
     * 头部的处理。作为 32 位整型,理论上 ignore_headers 最多可以表示 32 个
     * 需要跳过不予处理的头部
     */
    ngx_uint_t                       ignore_headers;
    /*
     * 以二进制位来表示一些错误码,如果处理上游响应时发现这些错误码,那么在
     * 没有将响应转发给下游客户端时,将会选择下一个上游服务器来重发请求
     */
    ngx_uint_t                       next_upstream;
    /*
     * 在 buffering 标志位为 1 的情况下转发响应时,将有可能把响应存放到临时文件
     * 中。在 ngx_http_upstream_t 中的 store 标志位为 1 时,store_access 表示
     * 所创建的目录、文件的权限.
     */
    ngx_uint_t                       store_access;
    ngx_uint_t                       next_upstream_tries;
    /*
     * 决定转发响应方式的标志位,buffering 为 1 时表示打开缓存,这时认为上游
     * 的网速快于下游的网速,会尽量地在内存或者磁盘中缓存来自上游的响应;如果
     * buffering 为 0,仅会开辟一块固定大小的内存块作为缓存来转发响应
     */
    ngx_flag_t                       buffering;
    ngx_flag_t                       request_buffering;
    ngx_flag_t                       pass_request_headers;
    ngx_flag_t                       pass_request_body;

    /*
     * 标志位,为 1 时表示与上游服务器交互时将不检查 Nginx 与下游客户端间的连接
     * 是否断开。也就是说,即使下游客户端主动关闭了连接,也不会中断与上游服务器
     * 间的交互.
     */
    ngx_flag_t                       ignore_client_abort;
    /*
     * 当解析上游响应的包头时,如果解析后设置到 headers_in 结构体中的 status_n 
     * 错误码大于 400,则会试图把它与 error_page 中指定的错误码相匹配,如果匹配
     * 上,则发送 error_page 中指定的响应,否则继续返回上游服务器的错误码.
     */
    ngx_flag_t                       intercept_errors;
    /*
     * buffering 标志位为 1 的情况下转发响应时才有意义。这时,如果 cyclic_temp_file 
     * 为 1,则会试图复用临时文件中已经使用过的空间。不建议将 cyclic_temp_file 
     * 设为 1.
     */
    ngx_flag_t                       cyclic_temp_file;
    ngx_flag_t                       force_ranges;

    /*
     * 在 buffering 标志位为 1 的情况下转发响应时,存放临时文件的路径
     */
    ngx_path_t                      *temp_path;

    /*
     * 不转发的头部。实际上是通过 ngx_http_upstream_hide_hash 方法,
     * 根据 hide_headers 和 pass_headers 动态数组构造出的需要隐藏的
     * HTTP 头部散列表
     */
    ngx_hash_t                       hide_headers_hash;
    /*
     * 当转发上游响应头部(ngx_http_upstream_t 中 headers_in 结构体中的头部)
     * 给下游客户端时,如果不希望某些头部转发给下游,就设置到 hide_headers
     * 动态数组中
     */
    ngx_array_t                     *hide_headers;
    /*
     * 当转发上游响应头部(ngx_http_upstream_t 中的 headers_in 结构体中的头部)
     * 给下游客户端时,upstream 机制默认不会转发如 "Data"、"Server" 之类的头部,
     * 如果确实希望直接转发它们到下游,就设置到 pass_headers 动态数组中
     */
    ngx_array_t                     *pass_headers;

    /*
     * 连接上游服务器时使用的本机地址
     */
    ngx_http_upstream_local_t       *local;

#if (NGX_HTTP_CACHE)
    ngx_shm_zone_t                  *cache_zone;
    ngx_http_complex_value_t        *cache_value;

    ngx_uint_t                       cache_min_uses;
    ngx_uint_t                       cache_use_stale;
    ngx_uint_t                       cache_methods;

    off_t                            cache_max_range_offset;

    ngx_flag_t                       cache_lock;
    ngx_msec_t                       cache_lock_timeout;
    ngx_msec_t                       cache_lock_age;

    ngx_flag_t                       cache_revalidate;
    ngx_flag_t                       cache_convert_head;
    ngx_flag_t                       cache_background_update;

    ngx_array_t                     *cache_valid;
    ngx_array_t                     *cache_bypass;
    ngx_array_t                     *cache_purge;
    ngx_array_t                     *no_cache;
#endif

    /*
     * 当 ngx_http_upstream_t 中的 store 标志位为 1 时,如果需要将上游的响应
     * 存放到文件中,store_lengths 将表示存放路径的长度,而 store_values 
     * 表示存放路径
     */
    ngx_array_t                     *store_lengths;
    ngx_array_t                     *store_values;

#if (NGX_HTTP_CACHE)
    signed                           cache:2;
#endif
    signed                           store:2;
    /*
     * 上面的 intercept_errors 标志位定义了 400 以上的错误码将会与 error_page
     * 比较后再行处理,实际上这个规则是可以有一个例外情况,如果将 intercept_404
     * 标志位设为 1,当上游返回 404 时会直接转发这个错误码给下游,而不会去与
     * errpr_page 进行比较.
     */
    unsigned                         intercept_404:1;
    /*
     * 为 1 时,将会根据 ngx_http_upstream_t 中 headers_in 结构体里的 X-Accel-Buffering
     * 头部(它的值会是 yes 和 no)来改变 buffering 标志位,当其值为 yes 时,buffering
     * 标志位为 1。因此,change_buffering 为 1 时将有可能根据上游服务器返回的响应头部,
     * 动态地决定是以上游网速优先还是以下游网速优先
     */
    unsigned                         change_buffering:1;

#if (NGX_HTTP_SSL || NGX_COMPAT)
    ngx_ssl_t                       *ssl;
    ngx_flag_t                       ssl_session_reuse;

    ngx_http_complex_value_t        *ssl_name;
    ngx_flag_t                       ssl_server_name;
    ngx_flag_t                       ssl_verify;
#endif

    ngx_str_t                        module;

    NGX_COMPAT_BEGIN(2)
    NGX_COMPAT_END
} ngx_http_upstream_conf_t;

2. upstream 流程概述

Nginx 访问上游服务器的流程大致可以分为以下 6 个阶段:

  • 启动 upstream 机制
  • 连接上游服务器
  • 向上游服务器发送请求
  • 接收上游服务器的响应包头
  • 处理接收到的响应包体
  • 结束请求

2.1 ngx_http_proxy_handler

参考 Nginx之最简单的反向代理机制分析 的分析,可知反向代理的具体执行过程是从该函数开始的。

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_CACHE)
    ngx_http_proxy_main_conf_t  *pmcf;
#endif

    /* 从内存池中创建 ngx_http_upstream_t 结构体 */
    if (ngx_http_upstream_create(r) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    /* 从内存池中创建 ngx_http_proxy_ctx_t 结构体 */
    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));
    if (ctx == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    /* 将 ngx_http_proxy_module 模块的上下文结构体 ngx_http_proxy_ctx_t
     * 存放到 r->ctx 数组中 module.ctx_index 下标处,r->ctx 数组
     * 存放着当前请求的各个 HTTP 模块的上下文结构体 */
    ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);

    /* 获取 ngx_http_proxy_module 模块的 loc 级别的配置 */
    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);

    /* 将上面 ngx_http_upstream_create 创建的 ngx_http_upstream_t 赋给 u */
    u = r->upstream;

    /* 假设 proxy_pass 指令为该配置:"proxy_pass http://rong;" */
    if (plcf->proxy_lengths == NULL) {
        /* plcf->vars 结构体存放着解析自 proxy_pass 指令的各个属性值,
         * 如域名、port 等 */
        ctx->vars = plcf->vars;
        /* 则 schema = "http://" */
        u->schema = plcf->vars.schema;
#if (NGX_HTTP_SSL)
        u->ssl = (plcf->upstream.ssl != NULL);
#endif

    } else {
        if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    /* output 是定义了向下游发送响应的方式 */
    u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;

    /* 使用 upstream 机制时的各种配置 */
    u->conf = &plcf->upstream;

#if (NGX_HTTP_CACHE)
    pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module);

    u->caches = &pmcf->caches;
    u->create_key = ngx_http_proxy_create_key;
#endif

    /* 
     * HTTP 模块实现的 create_request 方法用于构造发往上游服务器的请求 
     */
    u->create_request = ngx_http_proxy_create_request;
    /*
     * 与上游服务器的通信失败后,如果按照重试规则还需要再次向上游服务器发起
     * 连接,则会调用 reinit_request 方法
     */
    u->reinit_request = ngx_http_proxy_reinit_request;
    /*
     * 解析上游服务器返回响应的包头,返回 NGX_AGAIN 表示包头还没有接收完整,
     * 返回 NGX_HTTP_UPSTREAM_INVALID_HEADER 表示包头不合法,返回 NGX_ERROR
     * 表示出现错误,返回 NGX_OK 表示解析到完整的包头.
     */
    u->process_header = ngx_http_proxy_process_status_line;
    u->abort_request = ngx_http_proxy_abort_request;
    /*
     * 请求结束时会调用
     */
    u->finalize_request = ngx_http_proxy_finalize_request;
    r->state = 0;

    if (plcf->redirects) {
        /*
         * 在上游返回的响应出现 Location 或者 Refresh 头部表示重定向时,
         * 会通过 ngx_http_upstream_process_headers 方法调用到可由 HTTP
         * 模块实现的 rewrite_redirect 方法
         */
        u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
    }

    if (plcf->cookie_domains || plcf->cookie_paths) {
        u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;
    }

    /* 
     * 向下游转发上游的响应包体时,是否开启更大的内存及临时磁盘文件
     * 用于缓存来不及发送到下游的响应包体。默认为开启,即为 1 
     */
    u->buffering = plcf->upstream.buffering;

    /* 
     * 当向下游客户端转发响应时(ngx_http_request_t 结构体中的 subrequest_in_memory
     * 标志位为 0),如果打开了缓存且认为上游网速更快(conf 配置中的 buffering 标志
     * 位为 1),这时会使用 pipe 成员来转发响应。在使用这种方式转发响应时,必须由
     * HTTP 模块在使用 upstream 机制前构造 pipe 结构体,否则会出现严重的 coredump
     * 错误 
     */
    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
    if (u->pipe == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    /* 处理包体的方法 */
    u->pipe->input_filter = ngx_http_proxy_copy_filter;
    u->pipe->input_ctx = r;

    /* 
     * 处理包体前的初始化方法,其中 data 参数用于传递用户数据结构,它实际上
     * 就是下面的 input_filter_ctx 指针
     */
    u->input_filter_init = ngx_http_proxy_input_filter_init;
    /*
     * 处理包体的方法,其中 data 参数用于传递用户数据结构,它实际上就是下面的
     * input_filter_ctx 指针,而 bytes 表示本次接收到的包体长度。返回 NGX_ERROR
     * 时表示处理包体错误,请求需要结束,否则都将继续 upstream 流程
     */
    u->input_filter = ngx_http_proxy_non_buffered_copy_filter;
    /*
     * 用于传递 HTTP 模块自定义的数据结构,在 input_filter_init 和 input_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;
    }

    /* 这里先调用 ngx_http_upstream_init 函数启动 upstream 机制 */
    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    return NGX_DONE;
}

2.2 ngx_http_upstream_create

在把请求里的 ngx_http_request_t 结构体中的 upstream 成员(ngx_http_upstream_t 类型)创建并设置好,并且正确设置 upstream->conf 配置结构体(ngx_http_upstream_conf_t 类型)后,就可以启动 upstream 机制了。启动 upstream 的方法是调用 ngx_http_upstream_init 方法即可。

默认情况下请求的 upstream 成员只是 NULL 空指针,在设置 upstream 之前需要调用 ngx_http_upstream_create 方法从内存池中创建 ngx_http_upstream_t 结构体。实现如下:

ngx_int_t
ngx_http_upstream_create(ngx_http_request_t *r)
{
    ngx_http_upstream_t  *u;

    u = r->upstream;

    if (u && u->cleanup) {
        r->main->count++;
        ngx_http_upstream_cleanup(r);
    }

    /* 从内存池中为 ngx_http_upstream_t 结构体分配内存 */
    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
    if (u == NULL) {
        return NGX_ERROR;
    }

    /* 将 r->upstream 指向该新创建的 ngx_http_upstream_t 结构体首地址 */
    r->upstream = u;

    u->peer.log = r->connection->log;
    u->peer.log_error = NGX_ERROR_ERR;

#if (NGX_HTTP_CACHE)
    r->cache = NULL;
#endif

    /* 重置这两个变量的值 */
    u->headers_in.content_length_n = -1;
    u->headers_in.last_modified_time = -1;

    return NGX_OK;
}

ngx_http_upstream_create 方法只是创建 ngx_http_upstream_t 结构体而已,其中的成员还需要各个 HTTP 模块自行设置。

2.3 ngx_http_read_client_request_body

ngx_int_t
ngx_http_read_client_request_body(ngx_http_request_t *r,
    ngx_http_client_body_handler_pt post_handler)
{
    size_t                     preread;
    ssize_t                    size;
    ngx_int_t                  rc;
    ngx_buf_t                 *b;
    ngx_chain_t                out;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;

    /* 请求的引用计数加 1 */
    r->main->count++;

    if (r != r->main || r->request_body || r->discard_body) {
        r->request_body_no_buffering = 0;
        post_handler(r);
        return NGX_OK;
    }

    if (ngx_http_test_expect(r) != NGX_OK) {
        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        goto done;
    }

    /* 从内存池中创建 ngx_http_request_body_t 结构体 */
    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
    if (rb == NULL) {
        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        goto done;
    }

    /*
     * set by ngx_pcalloc():
     *
     *     rb->bufs = NULL;
     *     rb->buf = NULL;
     *     rb->free = NULL;
     *     rb->busy = NULL;
     *     rb->chunked = NULL;
     */

    rb->rest = -1;
    /* 由前知,这里传入的即为 ngx_http_upstream_init 函数,该函数用于启动 upstream 机制 */
    rb->post_handler = post_handler;

    /* 指向刚创建并初始化后的 ngx_http_request_body_t 结构体 */
    r->request_body = rb;

    /* 若来自客户端的请求消息中没有包含请求正文,则直接调用 post_handler 回调函数 */
    if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
        r->request_body_no_buffering = 0;
        post_handler(r);
        return NGX_OK;
    }
    
    /* 否则下面需要先处理客户端发来的请求消息中的请求正文 */

#if (NGX_HTTP_V2)
    if (r->stream) {
        rc = ngx_http_v2_read_request_body(r);
        goto done;
    }
#endif

    preread = r->header_in->last - r->header_in->pos;

    if (preread) {

        /* there is the pre-read part of the request body */

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http client request body preread %uz", preread);

        out.buf = r->header_in;
        out.next = NULL;

        rc = ngx_http_request_body_filter(r, &out);

        if (rc != NGX_OK) {
            goto done;
        }

        r->request_length += preread - (r->header_in->last - r->header_in->pos);

        if (!r->headers_in.chunked
            && rb->rest > 0
            && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
        {
            /* the whole request body may be placed in r->header_in */

            b = ngx_calloc_buf(r->pool);
            if (b == NULL) {
                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
                goto done;
            }

            b->temporary = 1;
            b->start = r->header_in->pos;
            b->pos = r->header_in->pos;
            b->last = r->header_in->last;
            b->end = r->header_in->end;

            rb->buf = b;

            r->read_event_handler = ngx_http_read_client_request_body_handler;
            r->write_event_handler = ngx_http_request_empty_handler;

            rc = ngx_http_do_read_client_request_body(r);
            goto done;
        }

    } else {
        /* set rb->rest */

        if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
            goto done;
        }
    }

    if (rb->rest == 0) {
        /* the whole request body was pre-read */
        r->request_body_no_buffering = 0;
        post_handler(r);
        return NGX_OK;
    }

    if (rb->rest < 0) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                      "negative request body rest");
        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        goto done;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    size = clcf->client_body_buffer_size;
    size += size >> 2;

    /* TODO: honor r->request_body_in_single_buf */

    if (!r->headers_in.chunked && rb->rest < size) {
        size = (ssize_t) rb->rest;

        if (r->request_body_in_single_buf) {
            size += preread;
        }

    } else {
        size = clcf->client_body_buffer_size;
    }

    rb->buf = ngx_create_temp_buf(r->pool, size);
    if (rb->buf == NULL) {
        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        goto done;
    }

    r->read_event_handler = ngx_http_read_client_request_body_handler;
    r->write_event_handler = ngx_http_request_empty_handler;

    rc = ngx_http_do_read_client_request_body(r);

done:

    if (r->request_body_no_buffering
        && (rc == NGX_OK || rc == NGX_AGAIN))
    {
        if (rc == NGX_OK) {
            r->request_body_no_buffering = 0;

        } else {
            /* rc == NGX_AGAIN */
            r->reading_body = 1;
        }

        r->read_event_handler = ngx_http_block_reading;
        post_handler(r);
    }

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        r->main->count--;
    }

    return rc;
}

该函数开始仅是从内存池中创建 ngx_http_request_body_t 结构体,初始化后赋给 r->request_body 指针。然后检测到客户端发来的请求消息并没有包含请求正文后,则直接调用 post_handler 函数,该函数指向 ngx_http_upstream_init,该函数用于启动 upstream 机制。

3. 启动 upstream

ngx_http_upstream_init 方法将会根据 ngx_http_upstream_conf_t 中的成员初始化 upstream,同时会开始连接上游服务器,以此展开整个 upstream 处理流程。ngx_http_upstream_init 方法实现如下:

void
ngx_http_upstream_init(ngx_http_request_t *r)
{
    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) {
        ngx_http_upstream_init_request(r);
        return;
    }
#endif

    /* 
     * 1. 首先检查请求对应于客户端的连接,这个连接上的读事件如果在定时器中,也就是
     * 说,读事件的 timer_set 标志位为 1,那么调用 ngx_del_timer 函数把这个读事件
     * 从定时器中移除。因为一旦启动 upstream 机制,就不应该对客户端的读操作带有
     * 超时时间的处理,请求的主要触发事件将以与上游服务器的连接为主.
     */
    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }

    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {

        /* 若当前连接的写事件还没有添加到 epoll 的监控机制中,这里则使用
         * 边缘模式将该写事件添加到 epoll 中 */
        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);
}

3.1 ngx_http_upstream_init_request

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;

#if (NGX_HTTP_CACHE)

    if (u->conf->cache) {
        ngx_int_t  rc;

        rc = ngx_http_upstream_cache(r, u);

        if (rc == NGX_BUSY) {
            r->write_event_handler = ngx_http_upstream_init_request;
            return;
        }

        r->write_event_handler = ngx_http_request_empty_handler;

        if (rc == NGX_ERROR) {
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }

        if (rc == NGX_OK) {
            rc = ngx_http_upstream_cache_send(r, u);

            if (rc == NGX_DONE) {
                return;
            }

            if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
                rc = NGX_DECLINED;
                r->cached = 0;
            }

            if (ngx_http_upstream_cache_background_update(r, u) != NGX_OK) {
                rc = NGX_ERROR;
            }
        }

        if (rc != NGX_DECLINED) {
            ngx_http_finalize_request(r, rc);
            return;
        }
    }

#endif

    /* 是否指定文件缓存路径的标志位 */
    u->store = u->conf->store;

    /* 
     * ignore_client_abort:为 1 时表示与上游服务器交互时将不检查 Nginx 与下游客户端
     * 间的连接是否断开。也就是说,即使下游服务器主动关闭了连接,也不会中断与上游
     * 服务器间的交互。
     * 
     * 2. 检查 ngx_http_upstream_conf_t 配置结构体中的 ignore_client_abort 标志位,
     * 如果 ignore_client_abort 为 1,如果为 1,就不需要设置 Nginx 与下游客户端
     * 之间 TCP 连接的检查方法。否则,进行如下设置(实际上,还需要让 store 标志位为
     * 0、请求 ngx_http_request_t 结构体中的 post_action 标志位为 0)
     */
    if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
        /* 这两个方法都是通过 ngx_http_upstream_check_broken_connection 
         * 方法检查 Nginx 与下游的连接是否正常,如果出现错误,就会立即终止连接 */
        r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;
        r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
    }

    if (r->request_body) {
        /*
         * request_bufs 以链表的方式把 ngx_buf_t 缓存区链接起来,它表示所有需要
         * 发送到上游服务器的请求内容。所以,HTTP 模块实现的 create_request 回调
         * 方法就在于构造 request_bufs 链表
         */
        u->request_bufs = r->request_body->bufs;
    }

    /* 
     * 3. 调用请求中 ngx_http_upstream_t 结构体里由某个 HTTP 模块实现的 create_request
     * 方法,构造发往上游服务器的请求(请求中的内容是设置到 request_bufs 缓冲区链表
     * 中的)。如果 create_request 方法没有返回 NGX_OK,则 upstream 机制结束,此时
     * 会调用 ngx_http_finalize_request 方法来结束请求
     * 由前面知,create_request 方法指向 ngx_http_proxy_create_request 函数 */
    if (u->create_request(r) != NGX_OK) {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

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

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    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) {
        /* 定义向下游发送响应包体的方法 */
        u->output.output_filter = ngx_chain_writer;
        u->output.filter_ctx = &u->writer;
    }

    u->writer.pool = r->pool;

    if (r->upstream_states == NULL) {

        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));
    }

    /*
     * 4. ngx_http_cleanup_t 是用于清理资源的结构体。这里 upstream 机制用到了
     * 该结构体。首先,调用 ngx_http_cleanup_add 方法向这个请求 main 成员指向的
     * 原始请求中的 cleanup 链表末尾添加一个新成员,然后把 handler 回调方法设为
     * ngx_http_upstream_cleanup,这意味着当请求结束时,一定会调用 
     * ngx_http_upstream_cleanup 方法
     */
    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;
    cln->data = r;
    u->cleanup = &cln->handler;

    if (u->resolved == NULL) {

        uscf = u->conf->upstream;

    } else {

#if (NGX_HTTP_SSL)
        u->ssl_name = u->resolved->host;
#endif

        host = &u->resolved->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++) {

            uscf = uscfp[i];

            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;
            }
        }

        if (u->resolved->sockaddr) {

            if (u->resolved->port == 0
                && u->resolved->sockaddr->sa_family != AF_UNIX)
            {
                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;
            }

            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;
        }

        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;
        }

        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_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;
    }

    u->upstream = uscf;

#if (NGX_HTTP_SSL)
    u->ssl_name = uscf->host;
#endif

    /* 该 init 指向 ngx_http_upstream_init_round_robin_peer  */
    if (uscf->peer.init(r, uscf) != NGX_OK) {
        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;
    }

    /* 5. 向上游服务器发起连接 */
    ngx_http_upstream_connect(r, u);
}

3.2 ngx_http_proxy_create_request

static ngx_int_t
ngx_http_proxy_create_request(ngx_http_request_t *r)
{
    size_t                        len, uri_len, loc_len, body_len;
    uintptr_t                     escape;
    ngx_buf_t                    *b;
    ngx_str_t                     method;
    ngx_uint_t                    i, unparsed_uri;
    ngx_chain_t                  *cl, *body;
    ngx_list_part_t              *part;
    ngx_table_elt_t              *header;
    ngx_http_upstream_t          *u;
    ngx_http_proxy_ctx_t         *ctx;
    ngx_http_script_code_pt       code;
    ngx_http_proxy_headers_t     *headers;
    ngx_http_script_engine_t      e, le;
    ngx_http_proxy_loc_conf_t    *plcf;
    ngx_http_script_len_code_pt   lcode;

    u = r->upstream;

    /* 获取该模块 loc 级别的配置信息结构体 */
    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);

#if (NGX_HTTP_CACHE)
    /* 若没有使能 cache 功能,则指向 plcf->headers */
    headers = u->cacheable ? &plcf->headers_cache : &plcf->headers;
#else
    headers = &plcf->headers;
#endif

    if (u->method.len) {
        /* HEAD was changed to GET to cache response */
        method = u->method;

    } else if (plcf->method) {
        if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) {
            return NGX_ERROR;
        }

    } else {
        /* 使用的是与客户端的同样的方法 */
        method = r->method_name;
    }

    /* 获取 ngx_http_proxy_module 模块的上下文结构体 */
    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);

    if (method.len == 4
        && ngx_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0)
    {
        ctx->head = 1;
    }

    /* 计算请求行的大小 */
    len = method.len + 1 + sizeof(ngx_http_proxy_version) - 1
          + sizeof(CRLF) - 1;

    escape = 0;
    loc_len = 0;
    unparsed_uri = 0;

    if (plcf->proxy_lengths && ctx->vars.uri.len) {
        uri_len = ctx->vars.uri.len;

    } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main)
    {
        unparsed_uri = 1;
        uri_len = r->unparsed_uri.len;

    } else {
        loc_len = (r->valid_location && ctx->vars.uri.len) ?
                      plcf->location.len : 0;

        if (r->quoted_uri || r->space_in_uri || r->internal) {
            escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
                                        r->uri.len - loc_len, NGX_ESCAPE_URI);
        }

        uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape
                  + sizeof("?") - 1 + r->args.len;
    }

    if (uri_len == 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "zero length URI to proxy");
        return NGX_ERROR;
    }

    len += uri_len;

    ngx_memzero(&le, sizeof(ngx_http_script_engine_t));

    ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes);
    ngx_http_script_flush_no_cacheable_variables(r, headers->flushes);

    if (plcf->body_lengths) {
        le.ip = plcf->body_lengths->elts;
        le.request = r;
        le.flushed = 1;
        body_len = 0;

        while (*(uintptr_t *) le.ip) {
            lcode = *(ngx_http_script_len_code_pt *) le.ip;
            body_len += lcode(&le);
        }

        ctx->internal_body_length = body_len;
        len += body_len;

    } else if (r->headers_in.chunked && r->reading_body) {
        ctx->internal_body_length = -1;
        ctx->internal_chunked = 1;

    } else {
        ctx->internal_body_length = r->headers_in.content_length_n;
    }

    le.ip = headers->lengths->elts;
    le.request = r;
    le.flushed = 1;

    while (*(uintptr_t *) le.ip) {
        while (*(uintptr_t *) le.ip) {
            lcode = *(ngx_http_script_len_code_pt *) le.ip;
            len += lcode(&le);
        }
        le.ip += sizeof(uintptr_t);
    }

    /* 遍历 headers_in.headers 链表,将在 headers.hash 表中没有
     * 找到对应的项的请求头添加到发往上游服务器的请求消息中 */
    if (plcf->upstream.pass_request_headers) {
        part = &r->headers_in.headers.part;
        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {
                if (part->next == NULL) {
                    break;
                }

                part = part->next;
                header = part->elts;
                i = 0;
            }

            if (ngx_hash_find(&headers->hash, header[i].hash,
                              header[i].lowcase_key, header[i].key.len))
            {
                continue;
            }

            len += header[i].key.len + sizeof(": ") - 1
                + header[i].value.len + sizeof(CRLF) - 1;
        }
    }

    /* 从内存池中分配一块临时缓存 */
    b = ngx_create_temp_buf(r->pool, len);
    if (b == NULL) {
        return NGX_ERROR;
    }

    /* 先检测 pool->chain 链表中是否有 ngx_chain_t,若没有,则
     * 重新生成一个 */
    cl = ngx_alloc_chain_link(r->pool);
    if (cl == NULL) {
        return NGX_ERROR;
    }

    cl->buf = b;


    /* the request line */
    /* 将请求行中最开始的请求方法拷贝到缓存中 */
    b->last = ngx_copy(b->last, method.data, method.len);
    *b->last++ = ' ';

    u->uri.data = b->last;

    if (plcf->proxy_lengths && ctx->vars.uri.len) {
        b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);

    } else if (unparsed_uri) {
        /* 拷贝 HTTP 方法后的 url */
        b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len);

    } else {
        if (r->valid_location) {
            b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
        }

        if (escape) {
            ngx_escape_uri(b->last, r->uri.data + loc_len,
                           r->uri.len - loc_len, NGX_ESCAPE_URI);
            b->last += r->uri.len - loc_len + escape;

        } else {
            b->last = ngx_copy(b->last, r->uri.data + loc_len,
                               r->uri.len - loc_len);
        }

        if (r->args.len > 0) {
            *b->last++ = '?';
            b->last = ngx_copy(b->last, r->args.data, r->args.len);
        }
    }

    u->uri.len = b->last - u->uri.data;

    /* 拷贝 HTTP 的版本 */
    if (plcf->http_version == NGX_HTTP_VERSION_11) {
        b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11,
                             sizeof(ngx_http_proxy_version_11) - 1);

    } else {
        b->last = ngx_cpymem(b->last, ngx_http_proxy_version,
                             sizeof(ngx_http_proxy_version) - 1);
    }
    
    /* 上面即构造好一个请求行 */

    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

    e.ip = headers->values->elts;
    e.pos = b->last;
    e.request = r;
    e.flushed = 1;

    le.ip = headers->lengths->elts;

    while (*(uintptr_t *) le.ip) {
        lcode = *(ngx_http_script_len_code_pt *) le.ip;

        /* skip the header line name length */
        (void) lcode(&le);

        if (*(ngx_http_script_len_code_pt *) le.ip) {

            for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
                lcode = *(ngx_http_script_len_code_pt *) le.ip;
            }

            e.skip = (len == sizeof(CRLF) - 1) ? 1 : 0;

        } else {
            e.skip = 0;
        }

        le.ip += sizeof(uintptr_t);

        while (*(uintptr_t *) e.ip) {
            code = *(ngx_http_script_code_pt *) e.ip;
            code((ngx_http_script_engine_t *) &e);
        }
        e.ip += sizeof(uintptr_t);
    }

    b->last = e.pos;


    if (plcf->upstream.pass_request_headers) {
        part = &r->headers_in.headers.part;
        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {
                if (part->next == NULL) {
                    break;
                }

                part = part->next;
                header = part->elts;
                i = 0;
            }

            if (ngx_hash_find(&headers->hash, header[i].hash,
                              header[i].lowcase_key, header[i].key.len))
            {
                continue;
            }

            /* 将 headers_in.headers 中不存在 headers->hash 散列表中的请求头
             * 添加到发往上游服务器的请求消息中 */
            b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);

            *b->last++ = ':'; *b->last++ = ' ';

            b->last = ngx_copy(b->last, header[i].value.data,
                               header[i].value.len);

            *b->last++ = CR; *b->last++ = LF;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http proxy header: \"%V: %V\"",
                           &header[i].key, &header[i].value);
        }
    }

    /* 空行 */
    /* add "\r\n" at the header end */
    *b->last++ = CR; *b->last++ = LF;

    /* 下面是构造请求正文,若有的话 */
    
    if (plcf->body_values) {
        e.ip = plcf->body_values->elts;
        e.pos = b->last;
        e.skip = 0;

        while (*(uintptr_t *) e.ip) {
            code = *(ngx_http_script_code_pt *) e.ip;
            code((ngx_http_script_engine_t *) &e);
        }

        b->last = e.pos;
    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http proxy header:%N\"%*s\"",
                   (size_t) (b->last - b->pos), b->pos);

    if (r->request_body_no_buffering) {

        u->request_bufs = cl;

        if (ctx->internal_chunked) {
            u->output.output_filter = ngx_http_proxy_body_output_filter;
            u->output.filter_ctx = r;
        }

    } else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {

        body = u->request_bufs;
        u->request_bufs = cl;

        while (body) {
            b = ngx_alloc_buf(r->pool);
            if (b == NULL) {
                return NGX_ERROR;
            }

            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));

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

            cl = cl->next;
            cl->buf = b;

            body = body->next;
        }

    } else {
        u->request_bufs = cl;
    }

    b->flush = 1;
    cl->next = NULL;

    return NGX_OK;
}

4. 与上游服务器建立连接

upstream 机制与上游服务器是通过 TCP 建立连接的,众所周知,建立 TCP 连接需要三次握手,而三次握手消耗的时间是不可控的。为了保证建立 TCP 连接的这个操作不会阻塞进程,Nginx 使用无阻塞的套接字来连接上游服务器。ngx_http_upstream_connect 方法就是用来连接上游服务器的,由于使用了非阻塞的套接字,当方法返回时与上游之间的 TCP 连接未必会成功建立,可能还需要等待上游服务器返回 TCP 的 SYN/ACK 包。因此,ngx_http_upstream_connect 方法主要负责发起建立连接这个动作,如果这个方法没有立刻返回成功,那么需要在 epoll 中监控这个套接字,当它出现可写事件时,就说明连接已经建立成功了。

ngx_http_upstream_connect 的实现如下:

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;

    rc = ngx_event_connect_peer(&u->peer);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http upstream connect: %i", rc);

    if (rc == NGX_ERROR) {
        ngx_http_upstream_finalize_request(r, u,
                                           NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    /* 保存的是当前连接的是 upstream 下的哪个服务器 */
    u->state->peer = u->peer.name;

    if (rc == NGX_BUSY) {
        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) {
        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;

    /*  
     * 5. 设置监听到读/写事件时调用的回调方法 
     */
    c->write->handler = ngx_http_upstream_handler;
    c->read->handler = ngx_http_upstream_handler;

    /*
     * 6. 将 upstream 机制的 write_event_handler 方法设为
     * ngx_http_upstream_send_request_handler。write_event_handler
     * 和 read_event_handler 的用法见 ngx_http_upstream_handler 方法。
     * 这一步骤实际上决定了向上游服务器发送请求的方法是
     * ngx_http_upstream_send_request_handler
     */
    u->write_event_handler = ngx_http_upstream_send_request_handler;
    /*
     * 7. 设置 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 */

    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)
    {
        /*
         * 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;
    u->request_body_sent = 0;

    /* 
     * 8. 上面连接上游服务器失败返回 NGX_AGAIN,因此这里会将
     * 写事件添加到定时器中 
     */
    if (rc == NGX_AGAIN) {
        ngx_add_timer(c->write, u->conf->connect_timeout);
        /* 若没有建立连接成功,则会从这里一路返回,然后将控制权交给
         * epoll,直到超时或监听到连接成功 */
        return;
    }

#if (NGX_HTTP_SSL)

    if (u->ssl && c->ssl == NULL) {
        ngx_http_upstream_ssl_init_connection(r, u, c);
        return;
    }

#endif

    /*
     * 9. 如果已经成功建立连接,则调用该函数向上游服务器发送请求。
     */
    ngx_http_upstream_send_request(r, u, 1);
}

4.1 ngx_event_connect_peer

ngx_int_t
ngx_event_connect_peer(ngx_peer_connection_t *pc)
{
    int                rc, type;
#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
    in_port_t          port;
#endif
    ngx_int_t          event;
    ngx_err_t          err;
    ngx_uint_t         level;
    ngx_socket_t       s;
    ngx_event_t       *rev, *wev;
    ngx_connection_t  *c;

    /* 这里是确定连接的是 upstream 中的哪个服务器,暂不分析 */
    rc = pc->get(pc, pc->data);
    if (rc != NGX_OK) {
        return rc;
    }

    type = (pc->type ? pc->type : SOCK_STREAM);

    /* 
     * 1. 调用 socket 建立一个 TCP 套接字 
     */
    s = ngx_socket(pc->sockaddr->sa_family, type, 0);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, "%s socket %d",
                   (type == SOCK_STREAM) ? "stream" : "dgram", s);

    if (s == (ngx_socket_t) -1) {
        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
                      ngx_socket_n " failed");
        return NGX_ERROR;
    }

    /*
     * 2. 由于 Nginx 的事件框架要求每一个连接都由一个 ngx_connection_t 结构体来承载,
     * 因此这一步将调用 ngx_get_connection,由 ngx_cycle_t 核心结构体中 free_connections
     * 指向的空闲连接池处获取一个 ngx_connection_t 结构体,作为承载 Nginx 与上游服务器
     * 间的 TCP 连接
     */
    c = ngx_get_connection(s, pc->log);

    if (c == NULL) {
        if (ngx_close_socket(s) == -1) {
            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
                          ngx_close_socket_n "failed");
        }

        return NGX_ERROR;
    }

    c->type = type;

    if (pc->rcvbuf) {
        if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
                       (const void *) &pc->rcvbuf, sizeof(int)) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
                          "setsockopt(SO_RCVBUF) failed");
            goto failed;
        }
    }

    /* 将该套接字设置为非阻塞 */
    if (ngx_nonblocking(s) == -1) {
        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
                      ngx_nonblocking_n " failed");

        goto failed;
    }

    if (pc->local) {

#if (NGX_HAVE_TRANSPARENT_PROXY)
        if (pc->transparent) {
            if (ngx_event_connect_set_transparent(pc, s) != NGX_OK) {
                goto failed;
            }
        }
#endif

#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
        port = ngx_inet_get_port(pc->local->sockaddr);
#endif

#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT)

        if (pc->sockaddr->sa_family != AF_UNIX && port == 0) {
            static int  bind_address_no_port = 1;

            if (bind_address_no_port) {
                if (setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT,
                               (const void *) &bind_address_no_port,
                               sizeof(int)) == -1)
                {
                    err = ngx_socket_errno;

                    if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) {
                        ngx_log_error(NGX_LOG_ALERT, pc->log, err,
                                      "setsockopt(IP_BIND_ADDRESS_NO_PORT) "
                                      "failed, ignored");

                    } else {
                        bind_address_no_port = 0;
                    }
                }
            }
        }

#endif

#if (NGX_LINUX)

        if (pc->type == SOCK_DGRAM && port != 0) {
            int  reuse_addr = 1;

            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
                           (const void *) &reuse_addr, sizeof(int))
                 == -1)
            {
                ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
                              "setsockopt(SO_REUSEADDR) failed");
                goto failed;
            }
        }

#endif

        if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) {
            ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno,
                          "bind(%V) failed", &pc->local->name);

            goto failed;
        }
    }

    if (type == SOCK_STREAM) {
        /* 设置读/写套接字数据的方法 */
        c->recv = ngx_recv;
        c->send = ngx_send;
        c->recv_chain = ngx_recv_chain;
        c->send_chain = ngx_send_chain;

        /* 开启 sendfile 功能 */
        c->sendfile = 1;

        if (pc->sockaddr->sa_family == AF_UNIX) {
            c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
            c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;

#if (NGX_SOLARIS)
            /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
            c->sendfile = 0;
#endif
        }

    } else { /* type == SOCK_DGRAM */
        c->recv = ngx_udp_recv;
        c->send = ngx_send;
        c->send_chain = ngx_udp_send_chain;
    }

    c->log_error = pc->log_error;

    rev = c->read;
    wev = c->write;

    rev->log = pc->log;
    wev->log = pc->log;

    pc->connection = c;

    /* 将原子变量 ngx_connection_counter 加 1,返回该原子变量 
     * ngx_connection_counter 之前的值 */
    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);

    /*
     * 3. 将该 TCP 套接字以期待可读、可写事件的方式添加到事件监控机制中。
     */
    if (ngx_add_conn) {
        if (ngx_add_conn(c) == NGX_ERROR) {
            goto failed;
        }
    }

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0,
                   "connect to %V, fd:%d #%uA", pc->name, s, c->number);

    /*
     * 4. 调用 connect 方法向上游服务器发起 TCP 连接,作为非阻塞套接字,
     * connect 方法可能立刻返回连接建立成功,也可能告诉用户继续等待上
     * 游服务器的响应,对 connect 连接是否建立成功的检查会在第 7 步之后
     * 进行。
     */
    rc = connect(s, pc->sockaddr, pc->socklen);

    if (rc == -1) {
        err = ngx_socket_errno;


        if (err != NGX_EINPROGRESS
#if (NGX_WIN32)
            /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
            && err != NGX_EAGAIN
#endif
            )
        {
            if (err == NGX_ECONNREFUSED
#if (NGX_LINUX)
                /*
                 * Linux returns EAGAIN instead of ECONNREFUSED
                 * for unix sockets if listen queue is full
                 */
                || err == NGX_EAGAIN
#endif
                || err == NGX_ECONNRESET
                || err == NGX_ENETDOWN
                || err == NGX_ENETUNREACH
                || err == NGX_EHOSTDOWN
                || err == NGX_EHOSTUNREACH)
            {
                level = NGX_LOG_ERR;

            } else {
                level = NGX_LOG_CRIT;
            }

            ngx_log_error(level, c->log, err, "connect() to %V failed",
                          pc->name);

            ngx_close_connection(c);
            pc->connection = NULL;

            return NGX_DECLINED;
        }
    }

    if (ngx_add_conn) {
        if (rc == -1) {

            /* NGX_EINPROGRESS */

            return NGX_AGAIN;
        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");

        wev->ready = 1;

        return NGX_OK;
    }

    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno,
                       "connect(): %d", rc);

        if (ngx_blocking(s) == -1) {
            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
                          ngx_blocking_n " failed");
            goto failed;
        }

        /*
         * FreeBSD's aio allows to post an operation on non-connected socket.
         * NT does not support it.
         *
         * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT
         */

        rev->ready = 1;
        wev->ready = 1;

        return NGX_OK;
    }

    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {

        /* kqueue */

        event = NGX_CLEAR_EVENT;

    } else {

        /* select, poll, /dev/poll */

        event = NGX_LEVEL_EVENT;
    }

    if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
        goto failed;
    }

    if (rc == -1) {

        /* NGX_EINPROGRESS */

        if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {
            goto failed;
        }

        return NGX_AGAIN;
    }

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");

    wev->ready = 1;

    return NGX_OK;

failed:

    ngx_close_connection(c);
    pc->connection = NULL;

    return NGX_ERROR;
}

4.2 ngx_http_upstream_handler

当监听到上游服务器接收该连接,即该套接字可写时,即会调用该函数。

static void
ngx_http_upstream_handler(ngx_event_t *ev)
{
    ngx_connection_t     *c;
    ngx_http_request_t   *r;
    ngx_http_upstream_t  *u;

    /*
     * 由事件的 data 成员取得 ngx_connection_t 连接。注意,这个连接
     * 并不是 Nginx 与客户端的连接,而是 Nginx 与上游服务器间的连接
     */
    c = ev->data;
    /*
     * 由连接的 data 成员取得 ngx_http_request_t 结构体
     */
    r = c->data;

    /*
     * 由请求的 upstream 成员取得表示 upstream 机制的 ngx_http_upstream_t 结构体
     */
    u = r->upstream;
    /*
     * 注意,ngx_http_request_t 结构体中的这个 connection 连接是客户端与 
     * Nginx 之间的连接
     */
    c = r->connection;

    ngx_http_set_log_request(c->log, r);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http upstream request: \"%V?%V\"", &r->uri, &r->args);

    if (ev->delayed && ev->timedout) {
        ev->delayed = 0;
        ev->timedout = 0;
    }

    /*
     * 当 Nginx 与上游服务器间 TCP 连接的可写事件被触发时,upstream 的 
     * write_event_handler 方法会被调用
     */
    if (ev->write) {
        u->write_event_handler(r, u);

    } else {
        /*
         * 当 Nginx 与上游服务器间 TCP 连接的可读事件被触发时,upstream
         * 的 read_event_handler 方法会被调用
         */
        u->read_event_handler(r, u);
    }

    ngx_http_run_posted_requests(c);
}

5. 发送请求到上游服务器

向上游服务器发送请求是一个阶段,因为请求的大小是未知的,所以发送请求的方法需要被 epoll 调度许多次后才可能发送完请求的全部内容。由上面的分析知,在 ngx_http_upstream_connect 函数中将 ngx_http_upstream_t 里的 write_event_handler 成员设为 ngx_http_upstream_send_request_handler 方法,即由该方法负责反复地发送请求。

ngx_http_upstream_send_request_handler 方法的具体实现如下:

static void
ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
    ngx_http_upstream_t *u)
{
    ngx_connection_t  *c;

    /* 获取与上游服务器间表示连接的 ngx_connection_t 结构体 */
    c = u->peer.connection;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http upstream send request handler");

    /* 写事件的 timedout 标志位为 1 时表示向上游服务器发送的请求已经超时 */
    if (c->write->timedout) {
        /* 将超时错误传递给 ngx_http_upstream_next 方法,该方法将会根据允许
         * 的错误重连策略决定:重新发起连接执行 upstream 请求,或者结束
         * upstream 请求 */
        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
        return;
    }

#if (NGX_HTTP_SSL)

    if (u->ssl && c->ssl == NULL) {
        ngx_http_upstream_ssl_init_connection(r, u, c);
        return;
    }

#endif

    /* header_sent 标志位为 1 表明上游服务器的响应需要直接转发给客户端,而且此时 Nginx
     * 已经把响应包头转发给客户端了 */
    if (u->header_sent) {
        /* 事实上,header_sent 为 1 时一定是已经解析完全部的上游响应包头,并且
         * 开始向下游发送 HTTP 的包头了。到此,是不应该继续向上游发送请求的,所以把 
         * write_event_handler 设为任何工作都没有做的 ngx_http_upstream_dummy_handler */
        u->write_event_handler = ngx_http_upstream_dummy_handler;

        /* 将写事件添加到 epoll 中 */
        (void) ngx_handle_write_event(c->write, 0);

        return;
    }

    /* 调用该方法向上游服务器发送请求 */
    ngx_http_upstream_send_request(r, u, 1);
}

5.1 ngx_http_upstream_send_request

static void
ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u,
    ngx_uint_t do_write)
{
    ngx_int_t          rc;
    ngx_connection_t  *c;

    c = u->peer.connection;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http upstream send request");

    if (u->state->connect_time == (ngx_msec_t) -1) {
        u->state->connect_time = ngx_current_msec - u->state->response_time;
    }

    if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {
        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
        return;
    }

    c->log->action = "sending request to upstream";

    /* 向上游服务器发送请求 */
    rc = ngx_http_upstream_send_request_body(r, u, do_write);

    if (rc == NGX_ERROR) {
        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
        return;
    }

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        ngx_http_upstream_finalize_request(r, u, rc);
        return;
    }

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

        } else if (c->write->timer_set) {
            ngx_del_timer(c->write);
        }

        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;
    }

    /* rc == NGX_OK */

    /* 这里表示已经完全将请求数据发送给上游服务器了,因此,置 
     * request_body_sent 标志位为 1 */
    u->request_body_sent = 1;

    /* 2. 检测写事件的 timer_set 标志位,为 1 表示写事件仍然在定时器中,
     * 因此需要把写事件从定时器中移除 */
    if (c->write->timer_set) {
        ngx_del_timer(c->write);
    }

    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
        if (ngx_tcp_push(c->fd) == NGX_ERROR) {
            ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
                          ngx_tcp_push_n " failed");
            ngx_http_upstream_finalize_request(r, u,
                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }

        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
    }

    /* 上面已经将请求数据发送给上游服务器了,因此这里将 write_event_handler 
     * 指向一个虚假的处理函数,该函数什么也不做 */
    u->write_event_handler = ngx_http_upstream_dummy_handler;

    /* 将该写事件添加到 epoll 中,ngx_http_upstream_send_request 方法结束 */
    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
        ngx_http_upstream_finalize_request(r, u,
                                           NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    /* 上面已经向上游服务器发送完全部请求,这时将准备开始处理响应,首先把读事件
     * 添加到定时器中检查接收响应是否超时,超时时间就是 ngx_http_upstream_conf_t
     * 配置结构体中的 read_timeout 成员 */
    ngx_add_timer(c->read, u->conf->read_timeout);

    /* 检测读事件标志位,如果 ready 为 1,则表示已经有响应可以读出  */
    if (c->read->ready) {
        /* 则调用该方法接收上游服务器的响应 */
        ngx_http_upstream_process_header(r, u);
        return;
    }
}

5.2 ngx_http_upstream_send_request_body

static ngx_int_t
ngx_http_upstream_send_request_body(ngx_http_request_t *r,
    ngx_http_upstream_t *u, ngx_uint_t do_write)
{
    ngx_int_t                  rc;
    ngx_chain_t               *out, *cl, *ln;
    ngx_connection_t          *c;
    ngx_http_core_loc_conf_t  *clcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http upstream send request body");

    if (!r->request_body_no_buffering) {

        /* buffered request body */

        /* request_sent 标志位为 1 表示是否已经传递了 request_bufs 缓冲区 */
        if (!u->request_sent) {
            /* 在第一次以 request_bufs 作为参数调用 ngx_output_chain 
             * 方法后,request_sent 会置为 1 */
            u->request_sent = 1;
            /* out 指向将要发送给上游服务器的请求数据构成的链表 */
            out = u->request_bufs;

        } else {
            out = NULL;
        }

        /* 1. 调用 ngx_out_chain 方法向上游服务器发送 ngx_http_upstream_t 结构体
         * 中的 request_bufs 链表,这个方法对于发送缓冲区构成的 ngx_chain_t 链表
         * 非常有用,它会把未发送完成的链表缓冲区保存下来,这样就不用每次调用时
         * 都携带上 request_bufs 链表。*/
        return ngx_output_chain(&u->output, out);
    }

    if (!u->request_sent) {
        u->request_sent = 1;
        out = u->request_bufs;

        if (r->request_body->bufs) {
            for (cl = out; cl->next; cl = out->next) { /* void */ }
            cl->next = r->request_body->bufs;
            r->request_body->bufs = NULL;
        }

        c = u->peer.connection;
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
            return NGX_ERROR;
        }

        r->read_event_handler = ngx_http_upstream_read_request_handler;

    } else {
        /* 进入到这里表示之前的请求数据还未发送完全,现在是再次触发
         * 写事件时,将未发送完全的数据发送该上游服务器,这里将 out
         * 置为 NULL,表示下面的 ngx_output_chain 函数不需要在传递参数 */
        out = NULL;
    }

    for ( ;; ) {

        if (do_write) {
            rc = ngx_output_chain(&u->output, out);

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

            while (out) {
                ln = out;
                out = out->next;
                ngx_free_chain(r->pool, ln);
            }

            if (rc == NGX_OK && !r->reading_body) {
                break;
            }
        }

        if (r->reading_body) {
            /* read client request body */

            rc = ngx_http_read_unbuffered_request_body(r);

            if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
                return rc;
            }

            out = r->request_body->bufs;
            r->request_body->bufs = NULL;
        }

        /* stop if there is nothing to send */

        if (out == NULL) {
            rc = NGX_AGAIN;
            break;
        }

        do_write = 1;
    }

    if (!r->reading_body) {
        if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
            r->read_event_handler =
                                  ngx_http_upstream_rd_check_broken_connection;
        }
    }

    return rc;
}

猜你喜欢

转载自www.cnblogs.com/jimodetiantang/p/9222035.html