nginx源码分析--模块分类

ngx-modules

Nginx 主要的模块大致可以分为四类:

  • handler – 协同完成客户端请求的处理、产生响应数据。比如模块, ngx_http_rewrite_module,
    ngx_http_log_module
    , ngx_http_static_module
  • filter – 对 handler 产生的响应数据做各种过滤处理。比如模块, ngx_http_not_modified_filter_module,
    ngx_http_header_filter_module
  • upstream – upstream 模块使 Nginx 可以充当反向代理,只对客户端请求进行转发。比 如,ngx_http_proxy_module,
    ngx_http_fastcgi_module
  • loadbalance – 在 Nginx 充当反向代理时,通过 loadbalance 模块的策略选择处理某次 请求的后端服务器节点。

比如,对一个普通的访问本地静态文件的请求处理,从 Nginx 收到请求并开始处理,到处 理结果的响应包体发送到网络上结束,整个过程的两个步骤 – 请求处理和响应处理 – 分别 由 handler 和 filter 处理完成。

严格来说,upstream 模块 (proxy, fastcgi 等),也是请求处理的一部分,它们从网络获 取请求的响应内容。

下面只分析一下 handler 模块和 filter 模块的注册和调用逻辑。

handler 模块

Nginx 将请求的处理阶段分为了下面列出的11个 phase ,如果某个 handler 模块需要在某 个 phase 中被调用时,需要在 postconfiguration 时将 handler 模块的入口函数注册到 Nginx 中。

    -----------http/ngx_http_core_module.h:86-------------
    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;

一个请求对应的 phase 会随着程序的执行发生变化。同时,在请求的实际处理过程中, 会因等待事件或者子请求等导致请求在不同的 phase 反复处理,但是在任意时刻,对某个 指定的客户端请求而言,它总是处于某个确定的 phase 中。这里先暂时将这个 phase 的转 化过程称为
phase machine。整个 phase machine 的退出点就是请求的完全处理结束。

处理函数注册

所有的 phase 和在各 phase 注册的入口函数,都临时保存在 ngx_http_core_modulemain conf 中的配置结构体
ngx_http_core_main_conf_tphases 成员中:

    ----------http/ngx_http_core_module.h:125-------------
    typedef struct {
        ngx_array_t                 handlers;
    } ngx_http_phase_t;

    typedef struct {
        ...
        ngx_http_phase_engine_t     phase_engine;
        ...
        ngx_http_phase_t            phases[NGX_HTTP_LOG_PHASE + 1]
    } ngx_http_core_main_conf_t;

ngx_http_rewrite_module 举例,其模块的 postconfiguration 对应回调函数
ngx_http_rewrite_init

    -----------http/modules/ngx_http_rewrite_module.c:104-----
    static ngx_http_module_t ngx_http_rewrite_module_ctx = {
        NULL,                               /* preconfiguration */
        ngx_http_rewrite_init,              /* postconfiguration */
        NULL,                               /* create main configuration */
        NULL,                               /* init main configuration */
        NULL,                               /* create server configuration */
        NULL,                               /* merge server configuration */
        ngx_http_rewrite_create_loc_conf,   /* create location configuration */
        ngx_http_rewrite_merge_loc_conf,    /* merge location configuration */
    };

ngx_http_rewrite_init 中,将 ngx_http_rewrite_module 的入口函数注册到了
NGX_HTTP_SERVER_REWRITE_PHASENGX_HTTP_REWRITE_PHASE 两个 phase 中:

    -----------http/modules/ngx_http_rewrite_module.c:266-------
    static ngx_int_t
    ngx_http_rewrite_init(ngx_conf_t *cf)
    {
        ...
        cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

        h = ngx_array_push(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers;
        ...
        *h = ngx_http_rewrite_handler;

        h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers;
        ...
        *h = ngx_http_rewrite_handler;
        ...
    }

然后,在所有 handler 模块的入口函数都注册完毕后,ngx_http_core_module 会将所有 入口函数封装成
ngx_http_phase_handler_t
类型变量后,线性化存储到 cmcf->phase_engine 中,同时在连续内存基础上,使用
next 成员再次生成一个链表 (通过数组索引链接)。ngx_http_phase_handler_t 类型变量的
checker 成员函数,决 定一个请求在 phase machine 中,在当前 phase 的当前 handler 函数处理完后,是该 调用同一 phase 中的下一个 handler 函数处理,还是直接进行下一个 phase (checker 可以认为是 phase 的调度函数)。

同时,在线性化过程中,还加入了 Nginx 内建 phase (find_config, post_rewrite,
post_access, try_files)的 checker 函数,这些 phase 原本身并不和任何模块的 handler 处理函数相对应 。

    ---------http/ngx_http_core_module.h:111--------------
    sturct ngx_http_phase_handler_s {
        ngx_http_phase_handler_pt   checker;
        ngx_http_handler_pt         handler;
        ngx_uint_t                  next;
    };

    typedef struct {
        ngx_http_phase_handler_t    *handlers;
        ngx_uint_t                  server_rewrite_index;
        ngx_uint_t                  location_rewrite_index;
    } ngx_http_phase_engine_t;

线性化处理由函数 ngx_http_init_phase_handlers 完成,它在所有 http 模块的 postconfiguration 被调用完成后执行:

    ---------http/ngx_http.c:328------
    for (m = 0; ngx_modules[m]; m++) {
        ...
        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }
    ...
    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

ngx_http_init_phase_handlers 执行完后,各 phase 中注册的回调函数被封装成 ngx_http_phase_handler_t 类型的变量,其字段的 checker 和 next 最终值总结如下:

    phase                   checker                             next
    ------------------------------------------------------------------------
    POST_READ       ngx_http_core_generic_phase         下一个 phase
    SERVER_REWRITE  ngx_http_core_generic_phase         下一个 phase
    FIND_CONFIG     ngx_http_core_find_config_phase     0
    REWRITE         ngx_http_core_generic_phase         下一个 phase
    POST_REWRITE    ngx_http_core_post_rewrite_phase    FIND_CONFIG phase
    PREACCESS       ngx_http_core_generic_phase         下一个 phase
    ACCESS          ngx_http_core_access_phase          下下一个 phase (跳过 post_access phrase)
    POST_ACCESS     ngx_http_core_post_access_phase     下一个 phase
    TRY_FILES       ngx_http_core_try_files_phase       0
    CONTENT         ngx_http_core_content_phase         NULL
    ------------------------------------------------------------------------

其中,各个 phase 的 checker 的行为如下:

    checker                             behavior
    ------------------------------------------------------------------------
    ngx_http_core_generic_phase         NGX_OK时,进入下一个 phase,
                                        NGX_DECLINED时,下一个 handler
    ngx_http_core_find_config_phase     下一个 handler
    ngx_http_core_post_rewrite_phase    下一个 phase,即 FIND_CONFIG_PHASE
    ngx_http_core_access_phase          NGX_OK时,进入下一个 phase,
                                        NGX_DECLINED, NGX_HTTP_FORBIDDEN,
                                        NGX_HTTP_UNAUTHORIZED时,下一个 handler
    ngx_http_core_post_access_phase     r->access_code 不为0时,结束请求
                                        否则,继续下一个 handler
    ngx_http_core_try_files_phase       下一个 handler
    ngx_http_core_content_phase         下一个 handler,或者结束请求
    ------------------------------------------------------------------------

另外一个需要注意的地方是,在 ngx_http_init_phase_handlers 中,只给 cmcf->phase_engine.handlers 分配了多余的
sizeof(void *) 个字节用于存储 handlers 结束标识 NULL:

    ---------http/ngx_http.c:488---------------
    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += cmcf->phases[i].handlers.nelts;
    }

    ph = ngx_pcalloc(cf->pool, n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));

而在随后的使用中,只检查了 ngx_http_phase_handler_t 的第一个字段,即 checker, 来判断是否到了
ngx_http_phase_handler_t 数组的末尾。这是有意为之,还是疏忽的地 方? 虽然代码可以正常运行,但是…

    --------http/ngx_http_core_module.c:836---------
    void
    ngx_http_core_run_phases(ngx_http_request_t *r)
    {
        ...
        while (ph[r->phase_handler].checker) {
            rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
            ...
        }
    } 

处理函数调用

在 Nginx 正常启动后,开始处理客户端请求。在处理客户端请求的过程中,就会根据请求 处于的 phase ,调用已注册到相应 phase 的回调函数,对请求进行处理。

当一个 HTTP 请求到达 Nginx 后,Nginx 会依次调用下面的处理函数 (先简单列出,等专 门再辟出一文对 HTTP 请求的“一生”进行详细描述):

    - ngx_http_init_connection
    - ngx_http_init_request
    - ngx_http_process_request_line
    - ngx_http_process_request_headers
    - ngx_http_process_request
    - ngx_http_handler
    - ngx_http_core_run_phasess

最后一个函数 ngx_http_core_run_phases 就是这个 phase machine 的驱动函数:

    -----------http/ngx_http_core_module.c:836--------
    void
    ngx_http_core_run_phases(ngx_http_request_t *r)
    {
        ...
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

        ph = cmcf->phase_engine.handlers;

        while (ph[r->phase_handler].checker) {
            rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
            if (rc == NGX_OK) {
                return;
            }
        }
    }

随后,整个请求处理的主要逻辑都是在这个 phase machine 中完成的。详细的处理逻辑 以后再分析,这篇关注的是 handler 模块的注册和调用逻辑。

filter 模块

filter 模块的初始化时间和 handler 模块一样,都是在模块的 postconfiguration 回 调函数中完成。但是,与 handler 模块函数的注册方式不同的时,filter 模块的入口函数 不需要区分 phase,同时,所有的 filter 入口函数组成
output filter chain。每个请 求的响应数据都经过 output filter chain 中的每个 filter 入口函数进行处理,然后 才会发向网络。

响应数据由响应头和响应体两部分组成,分别经过响应头对应的 header output filter chain 和响应体对应的 body output filter chain 处理。

处理函数注册

output filter chain,使用两个全局变量 ngx_http_top_header
ngx_http_top_body_filter
分别保存第一个 header filter 和 body filter 入口函数 的地址。每个 filter 模块的局部变量
ngx_http_next_header_filterngx_http_next_body_filter 保存当前 filter 模块执行完后需要调用的下一个 filter 模块的入口函数地址。这个 filter 链条在 Nginx初始化模块时完成, 比如ngx_http_addition_filter 模块的初始化函数如下:

    static ngx_int_t
    ngx_http_addition_filter_init(ngx_conf_t *cf)
    {
        ngx_http_next_header_filter = ngx_http_top_header_filter;
        ngx_http_top_header_filter = ngx_http_addition_header_filter;

        ngx_http_next_body_filter = ngx_http_top_body_filter;
        ngx_http_top_body_filter = ngx_http_addition_body_filter;

        return NGX_OK;
    }

header output filter chain 为例,Nginx在开始返回响应数据时,调用 ngx_http_top_header_filter
ngx_http_top_header_filter 指向的第一个 filter 模块的入口函数,第一个 filter 模块的入口函数处理完成以后,会调用其模块局部变量
ngx_http_next_header_filterngx_http_next_header_filter 指向的下一个 filter 模块的入口函数,以此类推,直到
ngx_http_header_filterngx_http_write_filter 模块将数据发回客户端,这个处理过程就完成了。

filter 模块入口函数的调用顺序由 filter 模块的初始化顺序决定,并且方向相反。模块 初始化顺序在 objs/ngx_modules.c 中定义:

     34 extern ngx_module_t  ngx_http_write_filter_module;
     35 extern ngx_module_t  ngx_http_header_filter_module;
     36 extern ngx_module_t  ngx_http_chunked_filter_module;
     37 extern ngx_module_t  ngx_http_range_header_filter_module;
     38 extern ngx_module_t  ngx_http_gzip_filter_module;
     39 extern ngx_module_t  ngx_http_postpone_filter_module;
     40 extern ngx_module_t  ngx_http_charset_filter_module;
     41 extern ngx_module_t  ngx_http_ssi_filter_module;
     42 extern ngx_module_t  ngx_http_userid_filter_module;
     43 extern ngx_module_t  ngx_http_headers_filter_module;
     44 extern ngx_module_t  ngx_http_copy_filter_module;
     45 extern ngx_module_t  ngx_http_range_body_filter_module;
     46 extern ngx_module_t  ngx_http_not_modified_filter_module;

可见,ngx_http_header_filter_modulengx_http_write_filter_module 最先被初 始化,那么,其提供的filter处理函数在整个响应处理过程中被最后调用。

body output filter chain 的生成方式和 header output filter chain 类似。

处理函数调用

在请求的响应内容准备完毕,开始向客户端发送响应数据时,Nginx 会使用 ngx_http_send_header将响应包头数据交给
header output filter chain 处理。包头 数据发送完成后,再使用 ngx_http_output_filter 将响应包体数据交给
body output filter chain 处理。

    -----------http/ngx_http_core_module.c:1702-----------
    ngx_int_t
    ngx_http_send_header(ngx_http_request_t *r)
    {
        if (r->err_status) {
            r->headers_out.status = r->err_status;
            r->headers_out.status_line.len = 0;
        }

        return ngx_http_top_header_filter(r);
    }

    ----------http/ngx_http_core_module.c:1713-------------
    ngx_int_t
    ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        ...
        rc = ngx_http_top_body_filter(r, in);
    }

随后的处理流程,就是按照 处理函数注册 中构造的 output filter chain 的顺序调用 各个 filter 模块的入口函数一步一步完成的。具体的逻辑,就和各个 filter 模块本身的 功能相关了。

nginx源码分析--模块分类,布布扣,bubuko.com

时间: 2024-12-14 18:10:21

nginx源码分析--模块分类的相关文章

nginx源码分析--nginx模块解析

nginx的模块非常之多,可以认为所有代码都是以模块的形式组织,这包括核心模块和功能模块,针对不同的应用场合,并非所有的功能模块都要被用到,附录A给出的是默认configure(即简单的http服务器应用)下被连接的模块,这里虽说是模块连接,但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序执行时再进行动态加载,nginx模块源文件会在生成nginx时就直接被编译到其二进制执行文件中,所以如果要选用不同的功能模块,必须对nginx做重新配置和编译.对于功能模块的选

nginx源码分析之模块初始化

在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要通过结合源码来分析模块的初始化过程. 稍微了解nginx的人都知道nginx是高度模块化的,各个功能都封装在模块中,而各个模块的初始化则是根据配置文件来进行的,下面我们会看到nginx边解析配置文件中的指令,边初始化指令所属的模块,指令其实就是指示怎样初始化模块的. 模块初始化框架 模块的初始化主要

[nginx] nginx源码分析--健康检查模块锁分析

健康检查模块 见前文:[nginx] nginx源码分析--健康检查模块 其中有一张框架图, 接下来的内容,将会利用到这个图中的内容. [classic_tong @ https:////www.cnblogs.com/hugetong/p/12274125.html ]  描述 我们知道nginx是多进程的,每个进程都保存了相同的配置.但是实际上, 并不需要每一个进程对每一个后端服务器进行. 于是健康检查模块在这里需要一个进程间同步机制,用来协商哪一个进程对 哪一个后端服务器进行检查. 接下来

nginx源码分析--从源码看nginx框架总结

nginx源码总结: 1)代码中没有特别绕特别别扭的编码实现,从变量的定义调用函数的实现封装,都非常恰当,比如从函数命名或者变量命名就可以看出来定义的大体意义,函数的基本功能,再好的架构实现在编码习惯差的人实现也会黯然失色,如果透彻理解代码的实现,领悟架构的设计初衷,觉得每块代码就想经过耐心雕琢一样,不仅仅实现了基本的功能给你,为其他人阅读也会提供很好的支持.细致恰当的命名规则就可以看出作者的功力. 2)更好更高的软件性能体现在架构设计上,好的架构会让软件更加稳定.容易维护.便于扩展.从核心模块

nginx源码分析--nginx外部信号 命令参数

nginx命令行参数 不像许多其他软件系统,Nginx 仅有几个命令行参数,完全通过配置文件来配置 -c </path/to/config> 为 Nginx 指定一个配置文件,来代替缺省的. -t 不运行,而仅仅测试配置文件.nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件. -v 显示 nginx 的版本. -V 显示 nginx 的版本,编译器版本和配置参数. nginx控制信号 可以使用信号系统来控制主进程.默认,nginx 将其主进程的 pid 写入到 /u

nginx源码分析--监听套接字的创建 套接字的监听 HTTP请求创建连接

作为一个web服务器,那么肯定是有监听套接字的,这个监听套接字是用于接收HTTP请求的,这个监听套接字的创建是根据配置文件的内容来创建的,在nginx.conf文件中有多少个地址就需要创建多少个监听套接字.这里不说各个结构体的构造 只说大体情况! 1).首先在main函数中调用了ngx_init_cycle()函数,在这个函数的最后调用了ngx_open_listening_sockets函数,这个函数负责将创建的监听套接字进行套接字选项的设置(比如非阻塞.接受发送的缓冲区.绑定.监听处理) 2

nginx源码分析--配置信息的继承&amp;合并

这里只讲述http{}模块下的配置: 在ngx_http_block()函数内(这个函数别调用时在ngx_inti_cycle内的ngx_conf_parse函数,这个函数遇到http命令时 回调ngx_http_block,开启http{}配置块的解读工作),针对每一个http模块,调用init_conf之后,有调用了ngx_http_merge_servers().这是为何! 首先明确几点:一个http{}配置块内可以包含多个server{}配置块,每个server{}配置块可以包含多个lo

Nginx源码分析:3张图看懂启动及进程工作原理

编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由陈科在高可用架构群分享.转载请注明来自高可用架构公众号「ArchNotes」.   导读:很多工程师及架构师都希望了解及掌握高性能服务器开发,阅读优秀源代码是一种有效的方式,nginx 是业界知名的高性能 Web 服务器实现,如何有效的阅读及理解 nginx?本文用图解的方式帮助大家来更好的阅读及理解 nginx 关键环节的实现.   陈科,十年行业从业经验,曾在浙江电信.阿里巴巴.华为.五八同城任开发工程及架构师等职,目前负责河狸

Nginx源码分析 - Nginx启动以及IOCP模型

Nginx 源码分析 - Nginx启动以及IOCP模型 版本及平台信息 本文档针对Nginx1.11.7版本,分析Windows下的相关代码,虽然服务器可能用linux更多,但是windows平台下的代码也基本相似 ,另外windows的IOCP完成端口,异步IO模型非常优秀,很值得一看. Nginx启动 曾经有朋友问我,面对一个大项目的源代码,应该从何读起呢?我给他举了一个例子,我们学校大一大二是在紫金港校区,到了 大三搬到玉泉校区,但是大一的时候也会有时候有事情要去玉泉办.偶尔会去玉泉,但