nginx-学习笔记5

handler模块

基本上作为第三方开发者最可能开发的就是三种类型的模块,即handler,filter和load-balancer。Handler模块就是接受来自客户端的请求并产生输出的模块。有些地方说upstream模块实际上也是一种handler模块,只不过它产生的内容来自于从后端服务器获取的,而非在本机产生的。

如果有多个handler模块都关联了同一个location,那么实际上只有一个handler模块真正会起作用。

handler模块处理的结果通常有三种情况: 处理成功,处理失败(处理的时候发生了错误)或者是拒绝去处理。在拒绝处理的情况下,这个location的处理就会由默认的handler模块来进行处理。

模块的基本结构

Nginx的配置信息分成了几个作用域(scope,有时也称作上下文),这就是main, server, 以及location。

例:

typedef struct
{
    ngx_str_t hello_string;
    ngx_int_t hello_counter;
}ngx_http_hello_loc_conf_t;

一个模块的配置指令是定义在一个静态数组中的。以hello module为例:

static ngx_command_t ngx_http_hello_commands[] = {
   {
        ngx_string("hello_string"),
        NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,
        ngx_http_hello_string,
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_hello_loc_conf_t, hello_string),
        NULL },

    {
        ngx_string("hello_counter"),
        NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
        ngx_http_hello_counter,
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_hello_loc_conf_t, hello_counter),
        NULL },

    ngx_null_command
};

ngx_command_t的定义,位于src/core/ngx_conf_file.h中

struct ngx_command_s {
    ngx_str_t             name;
    ngx_uint_t            type;
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    ngx_uint_t            conf;
    ngx_uint_t            offset;
    void                 *post;
};
name: 配置指令的名称。
type: 该配置的类型,其实更准确一点说,是该配置指令属性的集合。nginx提供了很多预定义的属性值(一些宏定义),通过逻辑或运算符可组合在一起,形成对这个配置指令的详细的说明。下面列出可在这里使用的预定义属性值及说明。
  • NGX_CONF_NOARGS:配置指令不接受任何参数。

  • NGX_CONF_TAKE1:配置指令接受1个参数。

  • NGX_CONF_TAKE2:配置指令接受2个参数。

  • NGX_CONF_TAKE3:配置指令接受3个参数。

  • NGX_CONF_TAKE4:配置指令接受4个参数。

  • NGX_CONF_TAKE5:配置指令接受5个参数。

  • NGX_CONF_TAKE6:配置指令接受6个参数。

  • NGX_CONF_TAKE7:配置指令接受7个参数。

    可以组合多个属性,比如一个指令即可以不填参数,也可以接受1个或者2个参数。那么就是NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_TAKE2。如果写上面三个属性在一起,你觉得麻烦,那么没有关系,nginx提供了一些定义,使用起来更简洁。

  • NGX_CONF_TAKE12:配置指令接受1个或者2个参数。

  • NGX_CONF_TAKE13:配置指令接受1个或者3个参数。

  • NGX_CONF_TAKE23:配置指令接受2个或者3个参数。

  • NGX_CONF_TAKE123:配置指令接受1个或者2个或者3参数。

  • NGX_CONF_TAKE1234:配置指令接受1个或者2个或者3个或者4个参数。

  • NGX_CONF_1MORE:配置指令接受至少一个参数。

  • NGX_CONF_2MORE:配置指令接受至少两个参数。

  • NGX_CONF_MULTI: 配置指令可以接受多个参数,即个数不定。

  • NGX_CONF_BLOCK:配置指令可以接受的值是一个配置信息块。也就是一对大括号括起来的内容。里面可以再包括很多的配置指令。比如常见的server指令就是这个属性的。

  • NGX_CONF_FLAG:配置指令可以接受的值是”on”或者”off”,最终会被转成bool值。

  • NGX_CONF_ANY:配置指令可以接受的任意的参数值。一个或者多个,或者”on”或者”off”,或者是配置块。

    最后要说明的是,无论如何,nginx的配置指令的参数个数不可以超过NGX_CONF_MAX_ARGS个。目前这个值被定义为8,也就是不能超过8个参数值。

    下面介绍一组说明配置指令可以出现的位置的属性。

  • NGX_DIRECT_CONF:可以出现在配置文件中最外层。例如已经提供的配置指令daemon,master_process等。

  • NGX_MAIN_CONF: http、mail、events、error_log等。

  • NGX_ANY_CONF: 该配置指令可以出现在任意配置级别上。

    对于我们编写的大多数模块而言,都是在处理http相关的事情,也就是所谓的都是NGX_HTTP_MODULE,对于这样类型的模块,其配置可能出现的位置也是分为直接出现在http里面,以及其他位置。

  • NGX_HTTP_MAIN_CONF: 可以直接出现在http配置指令里。

  • NGX_HTTP_SRV_CONF: 可以出现在http里面的server配置指令里。

  • NGX_HTTP_LOC_CONF: 可以出现在http server块里面的location配置指令里。

  • NGX_HTTP_UPS_CONF: 可以出现在http里面的upstream配置指令里。

  • NGX_HTTP_SIF_CONF: 可以出现在http里面的server配置指令里的if语句所在的block中。

  • NGX_HTTP_LMT_CONF: 可以出现在http里面的limit_except指令的block中。

  • NGX_HTTP_LIF_CONF: 可以出现在http server块里面的location配置指令里的if语句所在的block中。

set: 这是一个函数指针,当nginx在解析配置的时候,如果遇到这个配置指令,将会把读取到的值传递给这个函数进行分解处理。因为具体每个配置指令的值如何处理,只有定义这个配置指令的人是最清楚的。来看一下这个函数指针要求的函数原型。
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

该函数的返回值,处理成功时,返回NGX_OK,否则返回NGX_CONF_ERROR或者是一个自定义的错误信息的字符串。

再看一下这个函数被调用的时候,传入的三个参数。

  • cf: 该参数里面保存从配置文件读取到的原始字符串以及相关的一些信息。特别注意的是这个参数的args字段是一个ngx_str_t类型的数组,该数组的首个元素是这个配置指令本身,第二个元素是指令的第一个参数,第三个元素是第二个参数,依次类推。
  • cmd: 这个配置指令对应的ngx_command_t结构。
  • conf: 就是定义的存储这个配置值的结构体,比如在上面展示的那个ngx_http_hello_loc_conf_t。当解析这个hello_string变量的时候,传入的conf就指向一个ngx_http_hello_loc_conf_t类型的变量。用户在处理的时候可以使用类型转换,转换成自己知道的类型,再进行字段的赋值。

为了更加方便的实现对配置指令参数的读取,nginx已经默认提供了对一些标准类型的参数进行读取的函数,可以直接赋值给set字段使用。下面来看一下这些已经实现的set类型函数。

  • ngx_conf_set_flag_slot: 读取NGX_CONF_FLAG类型的参数。
  • ngx_conf_set_str_slot:读取字符串类型的参数。
  • ngx_conf_set_str_array_slot: 读取字符串数组类型的参数。
  • ngx_conf_set_keyval_slot: 读取键值对类型的参数。
  • ngx_conf_set_num_slot: 读取整数类型(有符号整数ngx_int_t)的参数。
  • ngx_conf_set_size_slot:读取size_t类型的参数,也就是无符号数。
  • ngx_conf_set_off_slot: 读取off_t类型的参数。
  • ngx_conf_set_msec_slot: 读取毫秒值类型的参数。
  • ngx_conf_set_sec_slot: 读取秒值类型的参数。
  • ngx_conf_set_bufs_slot: 读取的参数值是2个,一个是buf的个数,一个是buf的大小。例如: output_buffers 1 128k;
  • ngx_conf_set_enum_slot: 读取枚举类型的参数,将其转换成整数ngx_uint_t类型。
  • ngx_conf_set_bitmask_slot: 读取参数的值,并将这些参数的值以bit位的形式存储。例如:HttpDavModule模块的dav_methods指令。
conf: 该字段被NGX_HTTP_MODULE类型模块所用 (我们编写的基本上都是NGX_HTTP_MOUDLE,只有一些nginx核心模块是非NGX_HTTP_MODULE),该字段指定当前配置项存储的内存位置。实际上是使用哪个内存池的问题。因为http模块对所有http模块所要保存的配置信息,划分了main, server和location三个地方进行存储,每个地方都有一个内存池用来分配存储这些信息的内存。这里可能的值为 NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET或NGX_HTTP_LOC_CONF_OFFSET。当然也可以直接置为0,就是NGX_HTTP_MAIN_CONF_OFFSET。
offset: 指定该配置项值的精确存放位置,一般指定为某一个结构体变量的字段偏移。因为对于配置信息的存储,一般我们都是定义个结构体来存储的。那么比如我们定义了一个结构体A,该项配置的值需要存储到该结构体的b字段。那么在这里就可以填写为offsetof(A, b)。对于有些配置项,它的值不需要保存或者是需要保存到更为复杂的结构中时,这里可以设置为0。
post: 该字段存储一个指针。可以指向任何一个在读取配置过程中需要的数据,以便于进行配置读取的处理。大多数时候,都不需要,所以简单地设为0即可。


模块上下文结构

这是一个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);
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void       *(*create_srv_conf)(ngx_conf_t *cf);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

    void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
 preconfiguration: 在创建和读取该模块的配置信息之前被调用。
 postconfiguration: 在创建和读取该模块的配置信息之后被调用。
 create_main_conf: 调用该函数创建本模块位于http block的配置信息存储结构。该函数成功的时候,返回创建的配置对象。失败的话,返回NULL。
init_main_conf: 调用该函数初始化本模块位于http block的配置信息存储结构。该函数成功的时候,返回NGX_CONF_OK。失败的话,返回NGX_CONF_ERROR或错误字符串。
 create_srv_conf: 调用该函数创建本模块位于http server block的配置信息存储结构,每个server block会创建一个。该函数成功的时候,返回创建的配置对象。失败的话,返回NULL。
merge_srv_conf: 因为有些配置指令既可以出现在http block,也可以出现在http server block中。那么遇到这种情况,每个server都会有自己存储结构来存储该server的配置,但是在这种情况下http block中的配置与server block中的配置信息发生冲突的时候,就需要调用此函数进行合并,该函数并非必须提供,当预计到绝对不会发生需要合并的情况的时候,就无需提供。当然为了安全起见还是建议提供。该函数执行成功的时候,返回NGX_CONF_OK。失败的话,返回NGX_CONF_ERROR或错误字符串。
 create_loc_conf: 调用该函数创建本模块位于location block的配置信息存储结构。每个在配置中指明的location创建一个。该函数执行成功,返回创建的配置对象。失败的话,返回NULL。
merge_loc_conf: 与merge_srv_conf类似,这个也是进行配置值合并的地方。该函数成功的时候,返回NGX_CONF_OK。失败的话,返回NGX_CONF_ERROR或错误字符串。

 Nginx里面的配置信息都是上下一层层的嵌套的,对于具体某个location的话,对于同一个配置,如果当前层次没有定义,那么就使用上层的配置,否则使用当前层次的配置。

#define NGX_CONF_UNSET       -1
#define NGX_CONF_UNSET_UINT  (ngx_uint_t) -1
#define NGX_CONF_UNSET_PTR   (void *) -1
#define NGX_CONF_UNSET_SIZE  (size_t) -1
#define NGX_CONF_UNSET_MSEC  (ngx_msec_t) -1

又因为对于配置项的合并,逻辑都类似,也就是前面已经说过的,如果在本层次已经配置了,也就是配置项的值已经被读取进来了(那么这些配置项的值就不会等于上面已经定义的那些UNSET的值),就使用本层次的值作为定义合并的结果,否则,使用上层的值,如果上层的值也是这些UNSET类的值,那就赋值为默认值,否则就使用上层的值作为合并的结果。对于这样类似的操作,Nginx定义了一些宏操作来做这些事情,我们来看其中一个的定义。

#define ngx_conf_merge_uint_value(conf, prev, default)                       
    if (conf == NGX_CONF_UNSET_UINT) {                                       
        conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev;               
    }

以hello模块的模块上下文的定义为例:

static ngx_http_module_t ngx_http_hello_module_ctx = {
    NULL,                          /* preconfiguration */
    ngx_http_hello_init,           /* postconfiguration */

    NULL,                          /* create main configuration */
    NULL,                          /* init main configuration */

    NULL,                          /* create server configuration */
    NULL,                          /* merge server configuration */

    ngx_http_hello_create_loc_conf, /* create location configuration */
    NULL                        /* merge location configuration */
};

模块的定义

对于开发一个模块来说,我们都需要定义一个ngx_module_t类型的变量来说明这个模块本身的信息,从某种意义上来说,这是这个模块最重要的一个信息,它告诉了nginx这个模块的一些信息,上面定义的配置信息,还有模块上下文信息,都是通过这个结构来告诉nginx系统的,也就是加载模块的上层代码,都需要通过定义的这个结构,来获取这些信息。

ngx_module_t的定义如下:

typedef struct ngx_module_s      ngx_module_t;
struct ngx_module_s {
    ngx_uint_t            ctx_index;
    ngx_uint_t            index;
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            abi_compatibility;
    ngx_uint_t            major_version;
    ngx_uint_t            minor_version;
    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;
};

#define NGX_NUMBER_MAJOR  3
#define NGX_NUMBER_MINOR  1
#define NGX_MODULE_V1          0, 0, 0, 0,                              
    NGX_DSO_ABI_COMPATIBILITY, NGX_NUMBER_MAJOR, NGX_NUMBER_MINOR
#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0
原文地址:https://www.cnblogs.com/Anderson-Chaow/p/12318949.html