Nginx多阶段处理HTTP请求

来源:互联网 发布:有约束条件的优化问题 编辑:程序博客网 时间:2024/05/18 11:09

原文博客地址:Nginx Guts


Nginx服务器对HTTP请求的处理是分多个阶段的,每个阶段都有0个或者多个处理请求的函数会被调用。在Nginx源码中都有常量名称标志各个阶段。

下面是各个阶段的列表

typedef enum {    NGX_HTTP_POST_READ_PHASE = 0,    NGX_HTTP_SERVER_REWRITE_PHASE,    NGX_HTTP_FIND_CONFIG_PHASE,    NGX_HTTP_REWRITE_PHASE,    NGX_HTTP_POST_REWRITE_PHASE,    NGX_HTTP_PREACCESS_PHASE,    NGX_HTTP_ACCESS_PHASE,    NGX_HTTP_POST_ACCESS_PHASE,    NGX_HTTP_TRY_FILES_PHASE,    NGX_HTTP_CONTENT_PHASE,    NGX_HTTP_LOG_PHASE} ngx_http_phases;

  1. NGX_HTTP_SERVER_REWRITE_PHASE——“请求URI重写“阶段(比如内置的rewrite模块,支持if,break,return,rewrite,set等配置指令)
  2. NGX_HTTP_FIND_CONFIG_PHASE——根据请求位置查找配置信息(这个阶段不能自定义挂载处理函数)
  3. NGX_HTTP_REWRITE_PHASE——在location作用域内的URI重写
  4. NGX_HTTP_POST_REWRITE_PHASE——URI重写后的处理阶段(不能自定义挂载处理函数)
  5. NGX_HTTP_PREACCESS_PHASE——HTTP访问权限控制前一阶段(包含如limit_rate,limit_conn等模块)
  6. NGX_HTTP_ACCESS_PHASE——访问权限检查阶段(包含如auth_basic,或者第三方模块auth_request都在这个阶段)
  7. NGX_HTTP_POST_ACCESS_PHASE——访问权限检查后一阶段
  8. NGX_HTTP_TRY_FILES_PHASE——try_files指令的处理阶段
  9. NGX_HTTP_CONTENT_PHASE——内容生成阶段
  10. NGX_HTTP_LOG_PHASE——日志记录阶段

每个阶段你都可以注册任意数量的自定义处理器,除了下面几个

  • NGX_HTTP_FIND_CONFIG_PHASE——这个阶段没有处理器,只有一个查找配置信息的操作会被执行,还有添加Location头信息
  • NGX_HTTP_POST_ACCESS_PHASE——这个阶段没有处理器,只会解释处理ACCESS阶段的结果,不过配合倘若配置了satisfy指令(可以参照源代码)
  • NGX_HTTP_POST_REWRITE_PHASE——这个阶段没有处理器,只有内置的URI重写后的函数被调用
  • NGX_HTTP_TRY_FILES_PHASE——这个阶段只有nginx的try_files指令被调用

每个阶段都有自己的一系列处理函数,一旦把处理函数注册到对应的阶段,则函数可以返回下面的值。

  • NGX_OK——http请求已经被正确处理,需要传递到下一阶段
  • NGX_DECLINED——请求需要被发给本阶段的下一个处理器(handler)
  • NGX_AGAIN,NGX_DONE——请求已经被正确处理,而且需要被挂起知道网络事件(比如子请求结束,socket可写等)发生再次调用此处理器
  • NGX_ERROR,NGX_HTTP_..处理请求时出现错误

你需要获取ngx_http_core_module模块的配置结构体,然后将处理函数添加到需要的阶段(phase)

static ngx_int_tngx_http_sample_module_init(ngx_conf_t *cf){    ngx_http_handler_pt        *h;    ngx_http_core_main_conf_t  *cmcf;    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);    if (h == NULL) {        return NGX_ERROR;    }    *h = ngx_http_sample_module_handler;    return NGX_OK;}
数组phases对每个阶段都有个“入口”,每个“入口”包含一个处理器的指针,指向注册到本阶段的一组处理器。

处理器的调用顺序是反向的,最后注册到配置中的处理器会被最先调用。

从上面可以看出,请求处理的顺序和配置文件中的配置指令的先后顺序无关,无论配置文件中指令的顺序如何,各个阶段的处理函数都会按照预先的顺序执行。因此一个处理函数需要知道自己什么时候会被调用,何时需要返回NGX_DECLINED,而且保证减少性能损耗。

NGX_HTTP_ACCESS_PHASE阶段会调用一些处理器限制请求访问资源。这个阶段各个处理器的执行顺序由指令satisfy决定,处理器的返回值也有特定的意义。

  • NGX_OK——处理器允许请求访问URI资源
  • NGX_HTTP_FORBIDDEN,NGX_HTTP_UNAUTHORIZED——不允许访问资源

如果是配置了satisfy all,所有处理函数都需要返回NGX_OK保证能通过权限审核

如果配置了satisfy any,则至少有一个处理函数需要返回NGX_OK保证通过审核


Nginx在NGX_HTTP_CONTENT_PHASE阶段产生响应内容,所有请求都会到这个处理函数来,我们可以自定义自己的内容处理器,如果没有配置,则会调用默认的处理函数。

如何自定义自己的内容生成函数呢,下面是个例子

static char *ngx_http_sample_module_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){    ngx_http_core_loc_conf_t  *clcf;    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);    clcf->handler = ngx_http_sample_handler;    return NGX_CONF_OK;}
content handler 有下面几个特点:

  • 它是“不可恢复”的,即就算处理器返回NGX_AGAIN或者NGX_DONE也不会再次被调用,相反,处理器必须改变请求的read/write函数
  • 每个location都有自己专用的content handler
  • 如果自定义处理器返回NGX_DECLINED,则nginx会用默认的处理器去产生内容

那我们怎么知道自己的处理函数需要挂载在哪个阶段呢

虽然这篇博客不是对Nginx有所指责,但是确实是个问题。根据我的经验和nginx作者Igor Sysoev的建议,phases这个东西是从apache遗留下来的,对于模块开发者他们并不灵活,当然这可以改进,但是迄今为止也只能给点建议。

  • 如果你要放在phase阶段,则你的模块需要配合satisfy指令
  • 如果你想做权限控制,则放在pre-access阶段
  • 如果是URI重写或者操作变量,则放在rewrite阶段
  • 如果是产生响应内容,则放在content阶段,注意处理器的注册顺序
  • 如果是做日志处理,则放在logging阶段

什么时候需要用到content handler呢?

content phase handler和content handler有什么区别呢?

  • content phase handler是比较混乱的,所有请求到了content阶段都会调用这个特殊的处理器,而content handler则是在访问特定的location时才会被调用。
  • 每个location都可以访问多个content phase handler,而只能有一个content handler

一个结合这两种类型处理器的例子是nginx mogilefs module模块中处理PUT类型的请求(如下):

Main location中存在一个content phase handler,这个处理器有三个阶段(create_open命令,保存资源,create_close命令)每个阶段处理器都发起一个子请求,子请求结束时唤醒main content phase handler,在create_open和create_close命令中子请求会去访问有自己的content handler的location目录,这个处理函数通过upstrean模块和MogileFS的服务进行通信。

原创粉丝点击