菜鸟学习nginx之HTTP请求处理(1)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xxb249/article/details/85104001

上一篇主要介绍Nginx是如何处理HTTP Header。由于HTTP请求处理这部分代码是Nginx核心内容,打算用两篇文章深入介绍。本篇先介绍HTTP处理的11个阶段。

一、HTTP处理11阶段

1.1、为什么要有11阶段?

这个得从Nginx设计出发。首先Nginx是单线程且完全异步框架,Nginx把所有功能都统一抽象成模块,即“万物皆模块”。每个模块仅仅完成一个独立、简单的功能(说白点,就是一个功能模块不能占用cpu时间太长)。对于一个HTTP请求来说,需要我们考虑的内容很多,因此Nginx将HTTP请求划分成11个阶段,每个阶段只处理很简单的功能,例如:ACCESS访问权限。

1.2、11阶段定义

Nginx使用枚举定义11个阶段,具体每个阶段的含义已经在注释中表明。(注释摘自《深入理解Nginx模块开发与架构解析》)

typedef enum {
    /**
     * 在接收到完整的HTTP头部后处理的HTTP阶段
     */
    NGX_HTTP_POST_READ_PHASE = 0,

    /**
     * 在将请求的URI与location表达式匹配前,修改请求的URI(所谓的重定向)是一个独立的HTTP阶段
     */
    NGX_HTTP_SERVER_REWRITE_PHASE,

    /**
     * 根据请求的URI寻找匹配的location表达式,这个阶段只能由ngx_http_core_module模块实现,
     * 不建议其他HTTP模块重新定义这一阶段的行为
     */
    NGX_HTTP_FIND_CONFIG_PHASE,
    
    /**
     * 在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的location之后再修改请求的URI
     */
    NGX_HTTP_REWRITE_PHASE,

    /**
     * 这一阶段是用于在rewrite重写URL后,防止错误的nginx.conf配置导致死循环(递归地修改URI),
     * 因此,这一阶段仅由ngx_http_core_module模块处理。目前,控制死循环的方式很简单,首先检查
     * rewrite的次数,如果一个请求超过10次重定向,就认为进入了rewrite死循环,这时在
     * NGX_HTTP_POST_REWRITE_PHASE阶段就会向用户返回500,表示服务器内部错误
     */
    NGX_HTTP_POST_REWRITE_PHASE,

    /**
     * 表示在处理NGX_HTTP_ACCESS_PHASE阶段决定请求的访问权限前,HTTP模块可以介入的处理阶段
     */
    NGX_HTTP_PREACCESS_PHASE,

    /*
     * 这个阶段用于让HTTP模块判断是否允许这个请求访问Nginx服务器
     */
    NGX_HTTP_ACCESS_PHASE,
    
    /**
     * 在NGX_HTTP_ACCESS_PHASE阶段中,当HTTP模块的handler处理函数返回不允许访问的错误码时(实际就是
     * NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED),这里将负责向用户发送拒绝服务的错误响应。因此,
     * 这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾
     */
    NGX_HTTP_POST_ACCESS_PHASE,

    /**
     * 这个阶段完全是为try_files配置项而设立的,当HTTP请求访问静态文件资源时,
     * try_files配置项可以使这个请求顺序地访问多个静态文件资源,如果某一次访问失败,则继续访问
     * try_files中指定的下一个静态资源。这个功能完全是在
     * NGX_HTTP_TRY_FILES_PHASE阶段中实现的
     */
    NGX_HTTP_TRY_FILES_PHASE,

    /**
     * 用于处理HTTP请求内容的阶段,这是大部分HTTP模块最愿意介入的阶段
     */
    NGX_HTTP_CONTENT_PHASE,
    
    /**
     * 处理完请求后记录日志的阶段。例如,ngx_http_log_module模块就在这个阶段中加入了一个
     * handler处理方法,使得每个HTTP请求处理完毕后会记录access_log访问日志
     */
    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

1.3、流水线处理

既然Nginx设计了11个阶段,那么Nginx是如何管理的呢?对于流程化管理,目前业界有两种比较好的方式:状态机和流水线。Nginx采用流水线方式(Netty也采用流水线方式)。那么Nginx具体是如何实现的呢?

typedef struct ngx_http_phase_handler_s  ngx_http_phase_handler_t;

typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph);
/**
 * http阶段 流水线处理
 */
struct ngx_http_phase_handler_s {
    ngx_http_phase_handler_pt  checker; /* 当前阶段checker函数 */
    ngx_http_handler_pt        handler; /* 当前阶段处理函数 在checker函数中调用 */
    ngx_uint_t                 next; /* 下一处理阶段 */
};

说明:

1、checker函数只能由Nginx框架实现,用户不能修改。 

2、handler函数,可以由用户设置,handler回调函数只能在checker函数中调用。

3、next指定下一个阶段序号。Nginx虽然指定了11阶段,但是它允许跳跃执行。例如:当ACCESS阶段校验不通过,可直接结束当前流程,而不必继续向下执行。

4、checker回调函数设置在函数ngx_http_init_phase_handlers中。

1.4、如何介入

虽然Nginx定义了11个阶段,但并不是每一个阶段用户自定义模块都可以进入,具体可参考下面:

  名称 备注
用户模块可介入

NGX_HTTP_POST_READ_PHASE

NGX_HTTP_SERVER_REWRITE_PHASE

NGX_HTTP_REWRITE_PHASE

NGX_HTTP_PREACCESS_PHASE

NGX_HTTP_ACCESS_PHASE

NGX_HTTP_CONTENT_PHASE

NGX_HTTP_LOG_PHASE

用户自定义的模块,可以介入这些模块。其中NGX_HTTP_CONTENT_PHASE是最常见介入阶段。
用户模块不可介入

NGX_HTTP_FIND_CONFIG_PHASE

NGX_HTTP_POST_REWIRTE_PHASE

NGX_HTTP_POST_ACESS_PHASE

NGX_HTTP_TRY_FILES_PHASE

用户自定义模块不可以介入,这部分代码只能有HTTP框架介入。

那么如何介入呢?Nginx有两种方式:

1、设置postconfiguration回调函数,向全局的ngx_http_core_main_conf_t结构体的phase[NGX_HTTP_xx_PHASE]动态数组中添加ngx_http_handler_pt处理方法。例如ACCESS模块添加:

static ngx_int_t
ngx_http_access_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_ACCESS_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_access_handler;

    return NGX_OK;
}

2、设置ngx_http_core_loc_conf_t结构中handler回调函数。Nginx知道大部分都是介入NGX_HTTP_CONTENT_PHASE阶段,为了方便,Nginx在ngx_http_core_loc_conf_t中增了一个回调函数。而且这种方式是NGX_HTTP_CONTENT_PHASE阶段独有的。若仍然不清楚,可参考本篇《菜鸟学习Nginx之入门开发留言板》中函数ngx_http_webservice_login。

两种方式,第一种方式适用于所有阶段,而第二种方式仅适用于NGX_HTTP_CONTENT_PHASE阶段。

二、总结

本篇只简单介绍Nginx为了解决复杂的HTTP请求,定义了11个阶段以及如何介入11个阶段。下一篇正式介绍HTTP请求如何处理

猜你喜欢

转载自blog.csdn.net/xxb249/article/details/85104001