• nginx 访问第三方服务(1)


    nginx提供了两种全异步方式来与第三方服务通信,分别是upstream和subrequest。

    upstream:nginx为代理服务器,作消息透传。将第三方服务的内容原封不动的返回给用户。

    subrequest:为客户请求创建子请求。访问第三方服务只是为了获取某些信息,再根据这些信息构造响应返回给用户。

    upstream的使用方法

    ngx_http_request_t中有一个ngx_http_upstream_t类型的成员upstream

    struct ngx_http_request_s {
        ...
        ngx_http_upstream_t              *upstream;
    ... }

    nginx模块启动upstream机制的示意图

    upstream执行的一般流程如下:

    ngx_http_upstream_t结构体如下,重点关注已经写好注释的几个成员:
    struct ngx_http_upstream_s {
        ngx_http_upstream_handler_pt     read_event_handler;
        ngx_http_upstream_handler_pt     write_event_handler;
    
        ngx_peer_connection_t            peer;
    
        ngx_event_pipe_t                *pipe;
        //决定发送什么样的请求给上游服务器
        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_headers_in_t   headers_in;
        //指定上游服务器地址
        ngx_http_upstream_resolved_t    *resolved;
        //存储上游服务器发来的响应
        ngx_buf_t                        buffer;
        off_t                            length;
    
        ngx_chain_t                     *out_bufs;
        ngx_chain_t                     *busy_bufs;
        ngx_chain_t                     *free_bufs;
    
        ngx_int_t                      (*input_filter_init)(void *data);
        ngx_int_t                      (*input_filter)(void *data, ssize_t bytes);
        void                            *input_filter_ctx;
    
    #if (NGX_HTTP_CACHE)
        ngx_int_t                      (*create_key)(ngx_http_request_t *r);
    #endif
        //构造发往上游服务器的请求
        ngx_int_t                      (*create_request)(ngx_http_request_t *r);
        ngx_int_t                      (*reinit_request)(ngx_http_request_t *r);
        //收到上游服务器响应后的回调方法
        ngx_int_t                      (*process_header)(ngx_http_request_t *r);
        void                           (*abort_request)(ngx_http_request_t *r);
        //销毁upstream请求时调用
        void                           (*finalize_request)(ngx_http_request_t *r,
                                             ngx_int_t rc);
        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;
        ngx_str_t                        schema;
        ngx_str_t                        uri;
    
        ngx_http_cleanup_pt             *cleanup;
    
        unsigned                         store:1;
        unsigned                         cacheable:1;
        unsigned                         accel:1;
        unsigned                         ssl:1;
    #if (NGX_HTTP_CACHE)
        unsigned                         cache_status:3;
    #endif
        //向客户端转发上游服务器包体时才有用,buffering=1表示使用多个缓冲区及磁盘文件转发上游包体,buffering=0表示只使用缓冲区buffer转发数据
        unsigned                         buffering:1;
        unsigned                         keepalive:1;
    
        unsigned                         request_sent:1;
        unsigned                         header_sent:1;
    };
    upstream有3种方式处理上游的包体,这由ngx_http_request_t中的subrequest_in_memory决定:

    (1)subrequest_in_memory=1,upstream不转发响应包体,由input_filter方法处理
    (2)subrequest_in_memory=0,buffering=1  以多个缓冲区和磁盘文件转发包体
    (3)subrequest_in_memory=0,buffering=0  以固定的大小的缓冲区转发包体

     upstream限制性参数ngx_http_upstream_conf_t,其中3个超时时间是必须设置的,如下:

    typedef struct {
        ...//连接上游服务器的超时时间
        ngx_msec_t                       connect_timeout;
        //发送TCP包到上游服务器的超时时间
        ngx_msec_t                       send_timeout;
        //接收TCP包到上游服务器的超时时间
        ngx_msec_t                       read_timeout;
      ... }ngx_http_upstream_conf_t

    第三方服务器地址设置

    typedef struct {
        ngx_str_t                        host;
        in_port_t                        port;
        ngx_uint_t                       no_port; /* unsigned no_port:1 */
        //地址个数
        ngx_uint_t                       naddrs;
        
        in_addr_t                       *addrs;
        //地址
        struct sockaddr                 *sockaddr;
        socklen_t                        socklen;
    
        ngx_resolver_ctx_t              *ctx;
    } ngx_http_upstream_resolved_t;

    设置回调方法

    包括3个必须实现的回调方法

    ngx_int_t                      (*create_request)(ngx_http_request_t *r);
    ngx_int_t                      (*process_header)(ngx_http_request_t *r);
    void                           (*finalize_request)(ngx_http_request_t *r, ngx_int_t rc);

    5个可选的回调方法

    ngx_int_t                      (*input_filter_init)(void *data);
    ngx_int_t                      (*input_filter)(void *data, ssize_t bytes);
    ngx_int_t                      (*reinit_request)(ngx_http_request_t *r);
    void                           (*abort_request)(ngx_http_request_t *r);
    ngx_int_t                      (*rewrite_redirect)(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix);

    启动upstream机制

    直接执行ngx_http_upstream_init即可启动upstream机制,例如:

    static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
    {
        ...
        r->main->count++;
        ngx_http_upstream_init(r);
        return NGX_DONE;
    }

    通过返回NGX_DONE让HTTP框架停在执行请求的下一个阶段。这里执行r->main->count++是在告诉HTTP框架将当前请求的引用计数加1,暂时不要销毁该请求。

    回调方法执行场景时序图如下:

     reinit_request回调方法

    reinit_request被回调的原因只有1个:向上游服务器建立TCP连接失败,根据conf中的参数再次重连上游服务器,而这时就会调用reinit_request方法。

    finalize_request回调方法

    使用ngx_http_upstream_init启动upstream机制后,在各种原因导致该请求被销毁前,都会调用finalize_request中的方法。此方法必须实现。

    process_header回调方法

    用于解析上游服务器返回的基于HTTP的响应头部的,如果process_header返回NGX_AGAIN,意味着还没收到完整的头部,下次仍会调用process_header;如果返回NGX_OK,process_header不会再次调用

    input_filter_init和input_filter回调方法

    都用于处理上游服务器的响应包体,因为处理响应包体前可能需要做一些初始化工作,可以放在input_filter_init中。input_filter用于实际处理包体。以下场景比较适合重新实现input_filter_init和input_filter回调方法

    (1)转发上游响应到下游时,需要做一些特殊处理

    (2)无须在上、下游之间转发响应时,并不想完全接受到响应包体后才开始处理,希望能接受一部分就处理一部分,然后释放部分内存。

  • 相关阅读:
    剑指offer——其三
    剑指offer——其二
    剑指offer——其一
    数的读法 蓝桥杯练习题 Java
    2017小结及遇见更好的2018
    自然语言处理随笔(一)
    Shell实现Windows回收站功能
    解决win7下vc6.0打开添加文件错误 崩溃
    Could not load file or assembly Microsoft.Data.OData Version=5.2.0.0 error in Azure Cloud Worker Role using Table Storage
    String两种实例化方法的区别
  • 原文地址:https://www.cnblogs.com/yangang92/p/9142667.html
Copyright © 2020-2023  润新知