聊一聊nginx ngx_http_proxy_module模块

本文介绍了代理的基本概念,包括正向代理和反向代理,并重点讨论了nginx中的ngx_http_proxy_module模块,它是实现web代理服务的关键。proxy模块提供代理头设置和后端响应解析等功能,与upstream模块配合完成请求转发和内容缓存。文章详细阐述了proxy模块的处理流程,包括配置content_handler、请求转发、响应处理等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        什么是代理?代理是作为中间层,作用于上下游两端。将下游的“请求”转移提交到上游,将上游的“响应”提交给下游。代理不仅在设计模式等程序设计中有比较广泛的使用,同时在大型的系统工程中使用也比较广泛,在web服务器领域,代理出现的频率也是很高的。

        web服务中的代理有正向代理和反向代理两种,在大型的web服务中,反向代理是一种非常常用的技术,使用反向代理技能起到转发请求的作用,同时也能做到请求的控制及均衡。

        正向代理主要的机制是 客户端预先能知道代理服务器的地址及端口,通过客户端的设置来访问代理服务器,继而代理服务器产生作用以完成请求的响应处理,常用的ss vpn就是使用正向代理的方式来做到访问外部网站的,简单地理解,正向代理的代理服务器是由客户端"发现"的。

        反向代理的机制和正向代理在取得代理服务器地址端口的方式是不一样的,反向代理不需要客户端了解代理服务器,无需感知代理服务器的任何信息,通过访问域名通过解析服务得到代理服务器的地址和端口,从而完成访问,反向代理常见的一种功能是cdn(内容分发网络)中的缓存服务的关键功能。

        1.下图是正向代理的图解

           

        2.反向代理图解

        

        通过图解应该能容易看出正向代理和反向代理的区别。

         以nginx的proxy模块为例来介绍web代理服务的实现的细节。nginx的proxy模块提供了代理的头设置及对后端响应进行解析处理的功能,实际上proxy模块基本也是做了设置和解析处理的功能,具体的上游服务器的连接 接收及到向下游请求端的转发及web内容的缓存主要是nginx upjistream模块来提供的,nginx upstream实现的细节的介绍可以在点击打开链接这个连接中看到。

        nginx proxy模块提供了比较多的配置及设置功能,包括变量设置后端的server路径及协议、连接及响应的超时设置、buffer缓冲的设置、向上游获取数据的限速设置、缓存的控制等。会有一篇blog专门用来介绍nginx 代理功能的配置及细节的要点。本篇blog主要详细说明proxy模块的实现。

        

typedef struct {
    ngx_array_t                    caches;  /*http缓存数组 意味着可以配置多个缓存目录*/
} ngx_http_proxy_main_conf_t
   
struct ngx_http_proxy_rewrite_s { /*代理url重定向处理*/
    ngx_http_proxy_rewrite_pt      handler;      /*重定向处理函数指针*/

    union {
        ngx_http_complex_value_t   complex;    /*规则变量*/
#if (NGX_PCRE)
        ngx_http_regex_t          *regex;
#endif
    } pattern;

    ngx_http_complex_value_t       replacement;  /*url内容替换变量*/
}
typedef struct {   /*代理使用的http变量*/
    ngx_str_t                      key_start;        /**/
    ngx_str_t                      schema;           /*协议名*/
    ngx_str_t                      host_header;      /*host头*/
    ngx_str_t                      port;             /*端口*/
    ngx_str_t                      uri;              /*uri*/
} ngx_http_proxy_vars_t
typedef struct {  /*代理头结构 可以有多个http头*/
    ngx_array_t                   *flushes;
    ngx_array_t                   *lengths;
    ngx_array_t                   *values;
    ngx_hash_t                     hash;
} ngx_http_proxy_headers_t
typedef struct { /*代理location域配置*/
    ngx_http_upstream_conf_t       upstream;           /*upstream配置*/

    ngx_array_t                   *body_flushes;       /*代理包体数据 用于发送到上游服务器*/
    ngx_array_t                   *body_lengths;
    ngx_array_t                   *body_values;
    ngx_str_t                      body_source;

    ngx_http_proxy_headers_t       headers;            /*上面的代理头*/
#if (NGX_HTTP_CACHE)
    ngx_http_proxy_headers_t       headers_cache;      /*包含了缓存控制的代理头*/
#endif
    ngx_array_t                   *headers_source;     /*原始头*/

    ngx_array_t                   *proxy_lengths;      /*保存了脚本引擎信息 配合proxy_values使用*/
    ngx_array_t                   *proxy_values;

    ngx_array_t                   *redirects;           /*url重定向处理数组*/
    ngx_array_t                   *cookie_domains;      /*cookie信息配置*/
    ngx_array_t                   *cookie_paths;

    ngx_http_complex_value_t      *method;              /*访问的method变量 如GET HEAD POST...*/
    ngx_str_t                      location;            /*重定向的location*/
    ngx_str_t                      url;                 /*重定向的url*/

#if (NGX_HTTP_CACHE)
    ngx_http_complex_value_t       cache_key;          /*配置的缓存key值变量*/
#endif

    ngx_http_proxy_vars_t          vars;               /*代理所用的变量*/

    ngx_flag_t                     redirect;           /*是否重定向*/

    ngx_uint_t                     http_version;       /*http协议版本*/

    ngx_uint_t                     headers_hash_max_size;     /*hash表最大值*/
    ngx_uint_t                     headers_hash_bucket_size;  /*hash表表元素数量*/

#if (NGX_HTTP_SSL)                   
    ngx_uint_t                     ssl;   /*https代理访问*/
    ngx_uint_t                     ssl_protocols;
    ngx_str_t                      ssl_ciphers;
    ngx_uint_t                     ssl_verify_depth;
    ngx_str_t                      ssl_trusted_certificate;
    ngx_str_t                      ssl_crl;
    ngx_str_t                      ssl_certificate;
    ngx_str_t                      ssl_certificate_key;
    ngx_array_t                   *ssl_passwords;
#endif
} ngx_http_proxy_loc_conf_t
typedef struct {                      /*代理处理的上下文结构*/
    ngx_http_status_t              status;       /*http状态*/
    ngx_http_chunked_t             chunked;      /*chunked传输信息*/
    ngx_http_proxy_vars_t          vars;         /*变量*/
    off_t                          internal_body_length;  /*响应包体长度信息*/

    ngx_chain_t                   *free;         /*处理chain */
    ngx_chain_t                   *busy;

    unsigned                       head:1;       /*HEAD请求*/
    unsigned                       internal_chunked:1;  /*包体是chunked方式传输*/
    unsigned                       header_sent:1;   /*响应头数据是否已经发送到请求端*/
} ngx_http_proxy_ctx_t

nginx proxy模块处理流程    

1.配置content_handler处理函数 由proxy_pass设置来完成

static char *
ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
...

    if (plcf->upstream.upstream || plcf->proxy_lengths) { /*已经配置过了*/
        return "is duplicate";
    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_proxy_handler; /*设置content_handler为proxy_handler处理*/

    if (clcf->name.data[clcf->name.len - 1] == '/') { /*路径配置以"/"结束 自动跳转*/
        clcf->auto_redirect = 1;
    }

    value = cf->args->elts;

    url = &value[1];  /*取得配置的proxy_pass配置的路径*/

    n = ngx_http_script_variables_count(url); /*解析配置的路径 得到变量的数量*/

    if (n) {   /*代理的路径存在变量*/

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;
        sc.source = url;
        sc.lengths = &plcf->proxy_lengths;
        sc.values = &plcf->proxy_values;
        sc.variables = n;
        sc.complete_lengths = 1;
        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) { /*通过脚本引擎 编译变量*/
            return NGX_CONF_ERROR;
        }

#if (NGX_HTTP_SSL)
        plcf->ssl = 1;  /*安装了ssl功能 表示ssl可用*/
#endif

        return NGX_CONF_OK;
    }

    if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) { /*配置的路径以http起头 自动设置端口*/
        add = 7;
        port = 80;

    } else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) {/*同上*/

#if (NGX_HTTP_SSL)
        plcf->ssl = 1;

        add = 8;
        port = 443;
#else
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "https protocol requires SSL support");
        return NGX_CONF_ERROR;
#endif

    } else {  /*没有配置协议(http或者https)*/
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix");
        return NGX_CONF_ERROR;
    }

    ngx_memzero(&u, sizeof(ngx_url_t));
   /*设置upstream的url及端口 no_resolve为真 还未开始解析域名*/
    u.url.len = url->len - add;
    u.url.data = url->data + add;
    u.default_port = port;
    u.uri_part = 1;
    u.no_resolve = 1;
   /*增加一个upstream */
    plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
    if (plcf->upstream.upstream == NULL) {
        return NGX_CONF_ERROR;
    }
    /*设置proxy location域的schema变量*/
    plcf->vars.schema.len = add;
    plcf->vars.schema.data = url->data;
    plcf->vars.key_start = plcf->vars.schema;
    /*将upstream的host及端口设置到变量中*/
    ngx_http_proxy_set_vars(&u, &plcf->vars);
    /*location是当前location域的名称*/
    plcf->location = clcf->name;

    if (clcf->named
#if (NGX_PCRE)
        || clcf->regex
#endif
        || clcf->noname)
    {    //
        if (plcf->vars.uri.len) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "\"proxy_pass\" cannot ha
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值