添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
风流的鞭炮  ·  Filippi MAS 20 鞋·  4 月前    · 
刀枪不入的楼房  ·  A unified platform ...·  8 月前    · 
俊逸的黄瓜  ·  JS ...·  1 年前    · 
粗眉毛的创口贴  ·  文档中心 | ...·  1 年前    · 

Nginx 处理一个 HTTP 请求的全过程

前面给大家讲了 Nginx 是如何处理 HTTP请求头部的 ,接下来就到了真正处理 HTTP 请求的阶段了。先看下面这张图,这张图是 Nginx 处理 HTTP 请求的示意图,虽然简单,但是却很好的说明了整个过程。

  • Read Request Headers:解析请求头。
  • Identify Configuration Block:识别由哪一个 location 进行处理,匹配 URL。
  • Apply Rate Limits:判断是否限速。例如可能这个请求并发的连接数太多超过了限制,或者 QPS 太高。
  • Perform Authentication:连接控制,验证请求。例如可能根据 Referrer 头部做一些防盗链的设置,或者验证用户的权限。
  • Generate Content:生成返回给用户的响应。为了生成这个响应,做反向代理的时候可能会和上游服务(Upstream Services)进行通信,然后这个过程中还可能会有些子请求或者重定向,那么还会走一下这个过程(Internal redirects and subrequests)。
  • Response Filters:过滤返回给用户的响应。比如压缩响应,或者对图片进行处理。
  • Log:记录日志。
  • 以上这七个步骤从整体上介绍了一下处理流程,下面还会再说一下实际的处理过程。

    Nginx 处理 HTTP 请求的 11 个阶段

    下面介绍一下详细的 11 个阶段,每个阶段都可能对应着一个甚至多个 HTTP 模块,通过这样一个模块对比,我们也能够很好的理解这些模块具体是怎么样发挥作用的。

  • POST_READ:在 read 完请求的头部之后,在没有对头部做任何处理之前,想要获取到一些原始的值,就应该在这个阶段进行处理。这里面会涉及到一个 realip 模块。
  • SERVER_REWRITE:和下面的 REWRITE 阶段一样,都只有一个模块叫 rewrite 模块,一般没有第三方模块会处理这个阶段。
  • FIND_CONFIG:做 location 的匹配,暂时没有模块会用到。
  • REWRITE:对 URL 做一些处理。
  • POST_WRITE:处于 REWRITE 之后,也是暂时没有模块会在这个阶段出现。
  • 接下来是确认用户访问权限的三个模块:

  • PREACCESS:是在 ACCESS 之前要做一些工作,例如并发连接和 QPS 需要进行限制,涉及到两个模块:limt_conn 和 limit_req
  • ACCESS:核心要解决的是用户能不能访问的问题,例如 auth_basic 是用户名和密码,access 是用户访问 IP,auth_request 根据第三方服务返回是否可以去访问。
  • POST_ACCESS:是在 ACCESS 之后会做一些事情,同样暂时没有模块会用到。
  • 最后的三个阶段处理响应和日志:

  • PRECONTENT:在处理 CONTENT 之前会做一些事情,例如会把子请求发送给第三方的服务去处理,try_files 模块也是在这个阶段中。

  • CONTENT:这个阶段涉及到的模块就非常多了,例如 index, autoindex, concat 等都是在这个阶段生效的。

  • LOG:记录日志 access_log 模块。

    以上的这些阶段都是严格按照顺序进行处理的,当然,每个阶段中各个 HTTP 模块的处理顺序也很重要,如果某个模块不把请求向下传递,后面的模块是接收不到请求的。而且每个阶段中的模块也不一定所有都要执行一遍,下面就接着讲一下各个阶段模块之间的请求顺序。

    11 个阶段的顺序处理

    如下图所示,每一个模块处理之间是有序的,那么这个顺序怎么才能得到呢?其实非常简单,在源码 ngx_module.c 中,有一个数组 ngx_module_name ,其中包含了在编译 Nginx 的时候的 with 指令所包含的所有模块,它们之间的顺序非常关键,在数组中顺序是相反的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    char *ngx_module_names[] = {
    … …
    "ngx_http_static_module",
    "ngx_http_autoindex_module",
    "ngx_http_index_module",
    "ngx_http_random_index_module",
    "ngx_http_mirror_module",
    "ngx_http_try_files_module",
    "ngx_http_auth_request_module",
    "ngx_http_auth_basic_module",
    "ngx_http_access_module",
    "ngx_http_limit_conn_module",
    "ngx_http_limit_req_module",
    "ngx_http_realip_module",
    "ngx_http_referer_module",
    "ngx_http_rewrite_module",
    "ngx_http_concat_module",
    … …
    }

    灰色部分的模块是 Nginx 的框架部分去执行处理的,第三方模块没有机会在这里得到处理。

    在依次向下执行的过程中,也可能不按照这样的顺序。例如,在 access 阶段中,有一个指令叫 satisfy,它可以指示当有一个满足的时候就直接跳到下一个阶段进行处理,例如当 access 满足了,就直接跳到 try_files 模块进行处理,而不会再执行 auth_basic、auth_request 模块。

    在 content 阶段中,当 index 模块执行了,就不会再执行 auto_index 模块,而是直接跳到 log 模块。

    整个 11 个阶段所涉及到的模块和先后顺序如下图所示:

    下面开始详细讲解一下各个阶段。先来看下第一个阶段 postread 阶段,顾名思义,postread 阶段是在正式处理请求之前起作用的。

    postread 阶段

    postread 阶段,是 11 个阶段的第 1 个阶段,这个阶段刚刚获取到了请求的头部,还没有进行任何处理,我们可以拿到一些原始的信息。例如,拿到用户的真实 IP 地址

    问题:如何拿到用户的真实 IP 地址?

    我们知道,TCP 连接是由一个四元组构成的,在四元组中,包含了源 IP 地址。而在真实的互联网中,存在非常多的正向代理和反向代理。例如最终的用户有自己的内网 IP 地址,运营商会分配一个公网 IP,然后访问某个网站的时候,这个网站可能使用了 CDN 加速一些静态文件或图片,如果 CDN 没有命中,那么就会回源,回源的时候可能还要经过一个反向代理,例如阿里云的 SLB,然后才会到达 Nginx。

    我们要拿到的地址应该是运营商给用户分配的公网 IP 地址 115.204.33.1,对这个 IP 来进行并发连接的控制或者限速,而 Nginx 拿到的却是 2.2.2.2,那么怎么才能拿到真实的用户 IP 呢?

    HTTP 协议中,有两个头部可以用来获取用户 IP:

  • X-Forwardex-For 是用来传递 IP 的,这个头部会把经过的节点 IP 都记录下来
  • X-Real-IP:可以记录用户真实的 IP 地址,只能有一个
  • 拿到真实用户 IP 后如何使用?

    针对这个问题,Nginx 是基于变量来使用。

    例如 binary_remote_addr、remote_addr 这样的变量,其值就是真实的 IP,这样做连接限制也就是 limit_conn 模块才有意义,这也说明了,limit_conn 模块只能在 preaccess 阶段,而不能在 postread 阶段生效。

    realip 模块