handler模块

作为第三方开发者咱们最有可能开发的三种类型的模块别离是:handler、filter和load-balancer。配置文件应用location指令能够配置content handler模块,每个handler都有一次机会把本人关联到对应的location上。

模块根本构造

大家都晓得Nginx 的配置信息分成了几个作用域(scope,有时也称作上下文),这就是 main, server, 以及
location。同样的每个模块提供的配置指令也能够呈现在这几个作用域里。那对于这三个作
用域的配置信息,每个模块就须要定义三个不同的数据结构去进行存储。

typedef struct {    int count; //count    struct in_addr addr; //ip} ngx_pv_table;

模块配置指令

一个模块的配置指令是定义在一个动态数组中的。

struct ngx_command_s {    ngx_str_t             name; //名称    ngx_uint_t            type; //类型例如是main配置还是location配置以及有无参数    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //回调函数,解析到该命令之后,执行该函数    ngx_uint_t            conf;//是不是http module    ngx_uint_t            offset;//指定该配置项值的准确寄存地位,个别指定为某一个构造体变量的字段便宜。    void                 *post;};

模块上下文构造

这是一个 ngx_http_module_t 类型的动态变量。这个变量实际上是提供一组回调函数指针,这些函数有在创立存储配置信息的对象的函数,也有在创立前和创立后会调用的函数。这些函数都将被 nginx 在适合的工夫进行调用。

typedef struct {    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);//在创立和读取该模块的配置信息之前被调用    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);//在创立和读取该模块的配置信息之后被调用    void       *(*create_main_conf)(ngx_conf_t *cf);//调用该函数创立本模块位于 http block 的配置信息存储构造。该函数胜利的时候,返回创立的配置对象。失败的u话,返回 NULL    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);//调用该函数初始化本模块位于 http block 的配置信息存储构造。该函数胜利的时候,返回 NGX_CONF_OK。失败的话,返回 NGX_CONF_ERROR 或谬误字符串。    void       *(*create_srv_conf)(ngx_conf_t *cf);//调用该函数创立本模块位于 http server block 的配置信息存储构造,每个 server block 会创立一个。该函数胜利的时候,返回创立的配置对象。失败的话,返回 NULL。    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);//因为有些配置指令既能够呈现在 http block,也能够呈现在 http server block 中。那么遇到这种状况,每个 server都会有本人存储构造来存储该 server 的配置,然而在这种状况下 http block 中的配置与 server block 中的配置信息发生冲突的时候,就须要调用此函数进行合并,该函数并非必须提供,当预计到相对不会产生须要合并的状况的时候,就无需提供。当然为了平安起见还是倡议提供。该函数执行胜利的时候,返回 NGX_CONF_OK。失败的话,返回 NGX_CONF_ERROR 或谬误字符串    void       *(*create_loc_conf)(ngx_conf_t *cf);//调用该函数创立本模块位于 location block 的配置信息存储构造。每个在配置中指明的 location 创立一个。该函数执行胜利,返回创立的配置对象。失败的话,返回 NULL。    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);} ngx_http_module_t;

模块定义

对于开发一个模块来说,咱们都须要定义一个 ngx_module_t 类型的变量来阐明这个模块自身的信息,从某种意义上来说,这是这个模块最重要的一个信息,它通知了 nginx 这个模块的一些信息,下面定义的配置信息,还有模块上下文信息,都是通过这个构造来通知 nginx零碎的,也就是加载模块的下层代码,都须要通过定义的这个构造,来获取这些信息。咱们先来看下 ngx_module_t 的定义

struct ngx_module_s {    ngx_uint_t            ctx_index;    ngx_uint_t            index;    char                 *name;    ngx_uint_t            spare0;    ngx_uint_t            spare1;    ngx_uint_t            version;    const char           *signature;    void                 *ctx;    ngx_command_t        *commands;    ngx_uint_t            type;    ngx_int_t           (*init_master)(ngx_log_t *log);    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);    void                (*exit_thread)(ngx_cycle_t *cycle);    void                (*exit_process)(ngx_cycle_t *cycle);    void                (*exit_master)(ngx_cycle_t *cycle);    uintptr_t             spare_hook0;    uintptr_t             spare_hook1;    uintptr_t             spare_hook2;    uintptr_t             spare_hook3;    uintptr_t             spare_hook4;    uintptr_t             spare_hook5;    uintptr_t             spare_hook6;    uintptr_t             spare_hook7;};

个别咱们能够这么定义

ngx_module_t  ngx_epoll_module = {    NGX_MODULE_V1,    &ngx_epoll_module_ctx,               /* module context */    ngx_epoll_commands,                  /* module directives */    NGX_EVENT_MODULE,                    /* module type */    NULL,                                /* init master */    NULL,                                /* init module */    NULL,                                /* init process */    NULL,                                /* init thread */    NULL,                                /* exit thread */    NULL,                                /* exit process */    NULL,                                /* exit master */    NGX_MODULE_V1_PADDING};

handler编写步骤

typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r); 提供处理函数。

  1. 编写模块根本构造。包含模块的定义,模块上下文构造,模块的配置构造等。
  2. 实现 handler 的挂载函数。依据模块的需要抉择正确的挂载形式。
  3. 编写 handler 处理函数。模块的性能次要通过这个函数来实现。

http模块解决流程

nginx的http解决流程蕴含11个阶段,这11个阶段是精华

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_PRECONTENT_PHASE,    NGX_HTTP_CONTENT_PHASE,    NGX_HTTP_LOG_PHASE} ngx_http_phases;

这些阶段当中咱们最次要关注的是content阶段,每个阶段都有若干个handler函数。通过ngx_http_init_phase_handlers函数将这个handler函数填入引擎组。

        for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {            ph->checker = checker;            ph->handler = h[j];            ph->next = n;            ph++;        }//将handler填入引擎组的外围代码,后面通过switch case来判断每个阶段

具体handler的执行在函数ngx_http_core_phases(ngx_http_request_t *r).

voidngx_http_core_run_phases(ngx_http_request_t *r){    ngx_int_t                   rc;    ngx_http_phase_handler_t   *ph;    ngx_http_core_main_conf_t  *cmcf;    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]);        //r->phase_handler的变换在checker中执行,checker执行该阶段中的checker函数,checker函数中执行handler函数,checker自身用来判断是否满足执行handler函数的条件        if (rc == NGX_OK) {            return;        }    }}