欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

Nginx 中的 upstream 与 subrequest 机制

程序员文章站 2022-05-27 08:38:20
...
概述

Nginx 提供了两种全异步方式与第三方服务进行通信:upstreamsubrequestupstream 在与第三方服务器交互时(包括建立TCP 连接、发送请求、接收响应、关闭 TCP 连接),不会阻塞 Nginx 进程处理其他请求。subrequest 只是分解复杂请求的一种设计模式,它可以把原始请求分解为多个子请求,使得诸多请求协同完成一个用户请求,并且每个请求只关注一个功能。subrequest 访问第三方服务最终也是基于 upstream 实现的。

upstream 被定义为访问上游服务器,它把Nginx 定义为反代理服务器,首要功能是透传,其次才是以 TCP 获取第三方服务器的内容。NginxHTTP 反向代理模块是基于 upstream 方式实现的。subrequest 是子请求,也就是说subrequest 将会为用户创建子请求,即将一个复杂的请求分解为多个子请求,每个子请求负责一种功能项,而最初的原始请求负责构成并发送响应给用户。当subrequest 访问第三服务时,首先派生出子请求访问上游服务器,父请求在完全取得上游服务器的响应后再决定如何处理来自客户端的请求。

因此,若希望把是第三方服务的内容原封不动地返回给用户时,则使用 upstream 方式。若访问第三方服务是为了获取某些信息,再根据这些信息来构造响应并发给用户,则应使用 subrequest 方式。

upstream 使用方式

upstream 模块不产生自己的内容,而是通过请求后端服务器得到内容。Nginx 内部封装了请求并取得响应内容的整个过程,所以upstream 模块只需要开发若干回调函数,完成构造请求和解析响应等具体的工作。

ngx_http_request_t 结构体

首先了解 upstream 是如何嵌入到一个请求中,这里必须从请求结构体 ngx_http_request_t 入手,在该结构体中具有一个ngx_http_upstream_t 结构体类型的成员 upstream。请求结构体 ngx_http_request_t 定义在文件 src/http/ngx_http_request.h 中如下:

struct ngx_http_request_s {
    uint32_t                          signature;         /* "HTTP" */

    /* 客户端连接 */
    ngx_connection_t                 *connection;

    /*
     * 以下四个成员是保存模块对应的上下文结构指针;
     * ctx 对应的是自定义的上下文结构指针;
     * main_conf 对应的是main级别配置结构体的指针;
     * srv_conf 对应的是server级别配置结构体的指针;
     * loc_conf 对应的是location级别配置结构体的指针;
     */
    void                            **ctx;
    void                            **main_conf;
    void                            **srv_conf;
    void                            **loc_conf;

    /*
     * 以下两个是处理http请求;
     * 当http头部接收完毕,第一次在业务上处理http请求时,http框架提供的处理方法是ngx_http_process_request;
     * 若该方法无法一次性处理完该请求的全部业务时,当控制权归还给epoll事件模块后,该请求再次被回调,
     * 此时,将通过ngx_http_request_handler方法进行处理,而这个方法中对于可读或可写事件的处理就是由函数
     * read_event_handler或write_event_handler 来处理请求;
     */
    ngx_http_event_handler_pt         read_event_handler;
    ngx_http_event_handler_pt         write_event_handler;

#if (NGX_HTTP_CACHE)
    ngx_http_cache_t                 *cache;
#endif

    /* 若使用upstream机制,则需要以下的结构体 */
    ngx_http_upstream_t              *upstream;
    ngx_array_t                      *upstream_states;
                                         /* of ngx_http_upstream_state_t */

    /* 内存池 */
    ngx_pool_t                       *pool;
    /* 主要用于接收http请求头部内容的缓冲区 */
    ngx_buf_t                        *header_in;


    /*
     * 调用函数ngx_http_request_headers 接收并解析http请求头部完毕后,
     * 则把解析完成的每一个http头部加入到结构体headers_in的成员headers链表中,
     * 同时初始化该结构体的其他成员;
     */
    ngx_http_headers_in_t             headers_in;
    /*
     * http模块将待发送的http相应的信息存放在headers_out中,
     * 并期望http框架将headers_out中的成员序列化为http响应包体发送个客户端;
     */
    ngx_http_headers_out_t            headers_out;

    /* 接收请求包体的数据结构 */
    ngx_http_request_body_t          *request_body;

    /* 延迟关闭连接的时间 */
    time_t                            lingering_time;
    /* 当前请求初始化的时间 */
    time_t                            start_sec;
    ngx_msec_t                        start_msec;

    /*
     * 以下的 9 个成员是函数ngx_http_process_request_line在接收、解析http请求行时解析出的信息 */
    ngx_uint_t                        method;       /* 方法名称 */
    ngx_uint_t                        http_version; /* 协议版本 */

    ngx_str_t                         request_line; /* 请求行 */
    ngx_str_t                         uri;          /* 客户请求中的uri */
    ngx_str_t                         args;         /* uri 中的参数 */
    ngx_str_t                         exten;        /* 客户请求的文件扩展名 */
    ngx_str_t                         unparsed_uri; /* 没经过URI 解码的原始请求 */

    ngx_str_t                         method_name;  /* 方法名称字符串 */
    ngx_str_t                         http_protocol;/* 其data成员指向请求中http的起始地址 */

    /*
     * 存储待发送给客户的http响应;
     * out保存着由headers_out序列化后的表示http头部的TCP流;
     * 调用ngx_http_output_filter方法后,out还保存这待发送的http包体;
     */
    ngx_chain_t                      *out;
    /*
     * 当前请求可能是用户请求,或是派生的子请求;
     * main标识一序列相关的派生子请求的原始请求;
     * 即通过main与当前请求的地址对比来判断是用户请求还是派生子请求;
     */
    ngx_http_request_t               *main;
    /*
     * 当前请求的父亲请求,但不一定是原始请求 */
    ngx_http_request_t               *parent;
    /* 以下两个是与subrequest子请求相关的功能 */
    ngx_http_postponed_request_t     *postponed;
    ngx_http_post_subrequest_t       *post_subrequest;
    /* 连接子请求的链表 */
    ngx_http_posted_request_t        *posted_requests;

    /*
     * 全局结构体ngx_http_phase_engine_t定义了一个ngx_http_phase_handler_t回调方法的数组;
     * 而这里的phase_handler作为该数组的序列号表示指定数组中的回调方法,相当于数组的下标;
     */
    ngx_int_t                         phase_handler;
    /*
     * 表示NGX_HTTP_CONTENT_PHASE阶段提供给http模块请求的一种方式,它指向http模块实现的请求处理方法 */
    ngx_http_handler_pt               content_handler;
    /*
     * 在NGX——HTTP_CONTENT_PHASE阶段需要判断请求是否具有访问权限时,
     * 可通过access_code来传递http模块的handler回调方法的返回值来判断,
     * 若为0表示具备权限,否则不具备;
     */
    ngx_uint_t                        access_code;

    ngx_http_variable_value_t        *variables;

#if (NGX_PCRE)
    ngx_uint_t                        ncaptures;
    int                              *captures;
    u_char                           *captures_data;
#endif

    /* 限制当前请求的发送的速率 */
    size_t                            limit_rate;
    size_t                            limit_rate_after;

    /* http响应的长度,不包括http响应头部 */
    /* used to learn the Apache compatible response length without a header */
    size_t                            header_size;

    /* http请求的长度,包括http请求头部、http请求包体 */
    off_t                             request_length;

    /* 表示错误状态标志 */
    ngx_uint_t                        err_status;

    /* http 连接 */
    ngx_http_connection_t            *http_connection;
#if (NGX_HTTP_SPDY)
    ngx_http_spdy_stream_t           *spdy_stream;
#endif

    /* http日志处理函数 */
    ngx_http_log_handler_pt           log_handler;

    /* 释放资源 */
    ngx_http_cleanup_t               *cleanup;

    /* 以下都是一些标志位 */
    /* 派生子请求 */
    unsigned                          subrequests:8;
    /* 作为原始请求的引用计数,每派生一个子请求,原始请求的成员count会增加1 */
    unsigned                          count:8;
    /* 阻塞标志位 */
    unsigned                          blocked:8;

    /* 标志位:为1表示当前请求是异步IO方式 */
    unsigned                          aio:1;

    unsigned                          http_state:4;

    /* URI with "/." and on Win32 with "//" */
    unsigned                          complex_uri:1;

    /* URI with "%" */
    unsigned                          quoted_uri:1;

    /* URI with "+" */
    unsigned                          plus_in_uri:1;

    /* URI with " " */
    unsigned                          space_in_uri:1;

    unsigned                          invalid_header:1;

    unsigned                          add_uri_to_alias:1;
    unsigned                          valid_location:1;
    unsigned                          valid_unparsed_uri:1;
    /* 标志位:为1表示URI已经被重写 */
    unsigned                          uri_changed:1;
    /* 表示URI被重写的次数 */
    unsigned                          uri_changes:4;

    unsigned                          request_body_in_single_buf:1;
    unsigned                          request_body_in_file_only:1;
    unsigned                          request_body_in_persistent_file:1;
    unsigned                          request_body_in_clean_file:1;
    unsigned                          request_body_file_group_access:1;
    unsigned                          request_body_file_log_level:3;

    unsigned                          subrequest_in_memory:1;
    unsigned                          waited:1;

#if (NGX_HTTP_CACHE)
    unsigned                          cached:1;
#endif

#if (NGX_HTTP_GZIP)
    unsigned                          gzip_tested:1;
    unsigned                          gzip_ok:1;
    unsigned                          gzip_vary:1;
#endif

    unsigned                          proxy:1;
    unsigned                          bypass_cache:1;
    unsigned                          no_cache:1;

    /*
     * instead of using the request context data in
     * ngx_http_limit_conn_module and ngx_http_limit_req_module
     * we use the single bits in the request structure
     */
    unsigned                          limit_conn_set:1;
    unsigned                          limit_req_set:1;

#if 0
    unsigned                          cacheable:1;
#endif

    unsigned                          pipeline:1;
    unsigned                          chunked:1;
    unsigned                          header_only:1;
    unsigned                          keepalive:1;
    unsigned                          lingering_close:1;
    unsigned                          discard_body:1;
    unsigned                          internal:1;
    unsigned                          error_page:1;
    unsigned                          ignore_content_encoding:1;
    unsigned                          filter_finalize:1;
    unsigned                          post_action:1;
    unsigned                          request_complete:1;
    unsigned                          request_output:1;
    unsigned                          header_sent:1;
    unsigned                          expect_tested:1;
    unsigned                          root_tested:1;
    unsigned                          done:1;
    unsigned                          logged:1;

    unsigned                          buffered:4;

    unsigned                          main_filter_need_in_memory:1;
    unsigned                          filter_need_in_memory:1;
    unsigned                          filter_need_temporary:1;
    unsigned                          allow_ranges:1;
    unsigned                          single_range:1;

#if (NGX_STAT_STUB)
    unsigned                          stat_reading:1;
    unsigned                          stat_writing:1;
#endif

    /* used to parse HTTP headers */

    /* 当前的解析状态 */
    ngx_uint_t                        state;

    ngx_uint_t                        header_hash;
    ngx_uint_t                        lowcase_index;
    u_char                            lowcase_header[NGX_HTTP_LC_HEADER_LEN];

    u_char                           *header_name_start;
    u_char                           *header_name_end;
    u_char                           *header_start;
    u_char                           *header_end;

    /*
     * a memory that can be reused after parsing a request line
     * via ngx_http_ephemeral_t
     */

    u_char                           *uri_start;
    u_char                           *uri_end;
    u_char                           *uri_ext;
    u_char                           *args_start;
    u_char                           *request_start;
    u_char                           *request_end;
    u_char                           *method_end;
    u_char                           *schema_start;
    u_char                           *schema_end;
    u_char                           *host_start;
    u_char                           *host_end;
    u_char                           *port_start;
    u_char                           *port_end;

    unsigned                          http_minor:16;
    unsigned                          http_major:16;
};

若没有实现 upstream 机制,则请求结构体 ngx_http_request_t 中的upstream成员设置为NULL,否则必须设置该成员。首先看下 HTTP 模块启动upstream 机制的过程:

  1. 调用函数 ngx_http_upstream_create 为请求创建upstream
  2. 设置上游服务器的地址;可通过配置文件 nginx.conf 配置好上游服务器地址;也可以通过ngx_http_request_t 中的成员 resolved 设置上游服务器地址;
  3. 设置 upstream 的回调方法;
  4. 调用函数 ngx_http_upstream_init 启动upstream

upstream

上一篇: 关于smarty定义绝对路径在