Nginx 中的 upstream 与 subrequest 机制

本文详细介绍了Nginx的upstream和subrequest机制,upstream用于反向代理与第三方服务器交互,而subrequest用于分解复杂请求。upstream通过ngx_http_upstream_t等结构体实现,涉及TCP连接、请求构造和响应处理。subrequest则是父请求派生的子请求,用于协同完成用户请求。两者在处理第三方服务时有不同应用场景。
摘要由CSDN通过智能技术生成

概述

        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 对应的是自定义的上下文结构指针数组,若是HTTP框架,则存储所有HTTP模块上下文结构;
     * main_conf 对应的是main级别配置结构体的指针数组;
     * srv_conf 对应的是srv级别配置结构体的指针数组;
     * loc_conf 对应的是loc级别配置结构体的指针数组;
     */
    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;
    /* 相对于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;
    /* 阻塞标志位,仅用于aio */
    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;

    /* 决定是否转发响应,若该标志位为1,表示不转发响应,否则转发响应 */
    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
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值