nginx学习笔记(1):配置项的解析

前言

nginx.conf默认存放在目录/usr/local/nginx/conf下。

nginx配置文件的格式是由作者自己定义的,它没有采用像语法分析生成器LEMON那种经典的LALR(1)来描述配置信息,而是采用一种近似key-value对的形式。这种自定义格式的配置文件的好处就是自由、灵活,而坏处就是对于nginx的每一项配置信息都必须做针对性的解析和设置,因此我们很容易看到nginx源码里有大量篇幅的配置信息解析与赋值代码。

配置项格式

nginx配置文件是由多个配置项组成的,每一个配置项都有一个项目名和对应的项目值,也可能是由简单字符串和多个配置项组合而成的配置块的复杂结构。

我们可以将配置项归纳为两种:简单配置项和复杂配置项

1.简单配置项,例如:
worker_process 2;

2.复杂配置项,例如:
events {
    worker_connections 2000;
    use kqueue;
}

配置项相关数据结构

nginx利用ngx_command_s数据类型对所有的nginx配置项进行了统一的描述。

typedef struct ngx_command_s ngx_command_t;

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;
};

下面解释一下结构体中的各字段的含义:

(1)字段name指定配置项的名称。

(2)字段type将指定配置项的多种相关信息:

I.可指定该配置项的类型,例如:

NGX_CONF_FLAG:配置项携带的参数只能是1个,并且参数的值只能是on或者off

NGX_CONF_BLOCK:表示该配置项是复杂配置项,因此有一个由大括号组织起来的多值块。
例如,http、server、location等配置,它们的type都必须定义为NGX_CONF_BLOCK

NGX_CONF_ANY:不验证配置项携带的参数个数

......等等

II.可指定配置项的参数个数,例如:

NGX_CONF_NOARGS:配置项不携带任何参数  

NGX_CONF_TAKE1:配置项必须携带1个参数
NGX_CONF_TAKE2:配置项必须携带2个参数
......

NGX_CONF_TAKE12:配置项可以携带1个参数或2个参数
NGX_CONF_TAKE13:配置项可以携带1个参数或3个参数
......等等      

III.可指定配置项在哪些{ }配置块中出现,例如:

NGX_MAIN_CONF:配置项可以出现在全局配置中,即不属于任何{}配置块

NGX_EVENT_CONF: 配置项可以出现在events{}块内 

NGX_HTTP_MAIN_CONF:配置项可以出现在http{}块内

NGX_HTTP_SRV_CONF:配置项可以出现在server{}块内,然而该server块必须属于http{}块

......等等

(3)字段set指向配置指令处理回调函数(我们既可以自己实现一个回调方法来处理配置项,也可以使用nginx预设的14个解析配置项方法)。

预设的配置项解析回调函数,例如:

ngx_conf_set_flag_slot():如果nginx.conf文件中某个配置项的参数是on或者off(即希望配置项表达打开或者关闭某个功能的意思),
而且在Nginx模块的代码中使用ngx_flag_t变量来保存这个配置项的参数,就可以将set回调方法设为ngx_conf_set_flag_slot()。
当nginx.conf文件中参数是on时,代码中的ngx_flag_t类型变量将设为1,参数为off时则设为O 

......等等  

(4)字段conf用于指示配置项所处内存的相对偏移位置,仅在type中没有设置NGX_DIRECT_CONF和NGX_MAIN_CONF时才会生效。
它主要由模块NGX_HTTP_MODULE类型模块使用,取值如下:

NGX_HTTP_MAIN_CONF_OFFSET:使用create_main_conf方法产生的结构体来存储解析出的配置项参数

NGX_HTTP_SRV_CONF_OFFSET:使用create_srv_conf方法产生的结构体来存储解析出的配置项参数

NGX_HTTP_LOC_CONF_OFFSET:使用create_loc_conf方法产生的结构体来存储解析出的配置项参数

(5)offset表示当前配置项在整个存储配置项的结构体中的偏移位置(利用offsetof宏)。

(6)如果自定义了配置项的回调方法,那么post指针的用途完全由用户来定义。如果不使用它,那么设为NULL即可。如果想将一些数据结构或者方法的指针传过来,那么使用post也可以。


每个模块都把自己所需要的配置项的对应ngx_command_s结构体变量组成一个数组,并以ngx_xxx_xxx_commands的形式命名,该数组以元素ngx_null_command作为结束哨兵。

#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }

配置文件的基本解析流程

1.流程图:
nginx学习笔记(1):配置项的解析_第1张图片

2.先来看一下ngx_conf_parse()函数:

函数原型:
char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);

第一个参数为ngx_conf_t变量,第二个参数是保存配置文件路径的字符串。
它是一个间接的递归函数,也就是说虽然我们在该函数体内看不到直接的对其本身的调用,但是它执行的一些函数(比如ngx_conf_handler())内又会调用ngx_conf_parse()函数,因此形成递归,这一般在处理一些特殊配置指令或复杂配置项,比如指令include、events、http、 server、location等的处理时。

ngx_conf_parse()函数基本将配置内容的解析过程分为三个步骤:
判断当前解析状态;
读取配置标记token;
处理配置标记token,将配置值转换为nginx内对应控制变量的值。

(1)判断当前解析状态,当前解析状态有三种可能:
I.刚开始解析一个配置文件,即此时的参数filename指向一个配置文件路径字符串,需要函数ngx_conf_parse()打开该文件并获取相关的文件信息以便下面代码读取文件内容并进行解析,除了在上面介绍的nginx启动时开始主配置文件解析时属于这种情况,还有当遇到include指令时也将以这种状态调用ngx_conf_parse()函数,因为include指令表示一个新的配置文件要开始解析。状态标记为type = parse_file;。

II.开始解析一个配置块,即此时配置文件已经打开并且也已经对文件部分进行了解析,当遇到复杂配置项比如events、http等时,这些复杂配置项的处理函数又会递归的调用ngx_conf_parse()函数,此时解析的内容还是来自当前的配置文件,因此无需再次打开它,状态标记为type =parse_block;。

III.开始解析配置项,这在对用户通过命令行-g参数输入的配置信息进行解析时处于这种状态,如:
nginx -g ‘daemon on;’
nginx在调用ngx_conf_parse()函数对配置信息’daemon on;’进行解析时就是这种状态,状态标记为type = parse_param;。

(2)判断好解析状态之后,接下来就要读取配置内容,函数ngx_conf_read_token()就是来做这个事情的。

函数原型:
static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf);

ngx_conf_read_token()函数主要完成的工作如下:
I.读取文件内容,每次读取一个buf大小(4K),如果文件内容不足4K则全部读取到buf中
II.扫描buf中的内容,每次扫描一个token就会存入cf->args中,然后返回
III.返回后ngx_conf_parse()函数会调用*cf->handler和ngx_conf_handler(cf, rc)函数处理
IV.如果是复杂配置项,会调用上次执行的状态继续解析配置文件.

ngx_conf_read_token()函数的返回值决定了ngx_conf_parse()函数接下来的处理,对应关系如下表:
nginx学习笔记(1):配置项的解析_第2张图片

(3)对于简单/复杂配置项的处理,一般情况下,通过函数ngx_conf_handler()来进行的(也有特殊的情况,也就是配置项提供了自定义的处理函数)。

函数原型:
static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last);

第一个参数为ngx_conf_t变量,第二个参数记录的是最近一次token解析函数
ngx_conf_read_token()的返回值

3.函数ngx_conf_handler()也做了三件事情:
首先,它需要找到当前解析出来的配置项所对应的 ngx_command_s结构体,如果没找到配置项所对应的 ngx_command_s结构体,nginx就直接进行报错并退出程序。

其次,找到当前解析出来的配置项所对应的ngx_command_s结构体之后还需进行一些有效性验证,比如配置项的类型、位置、带参数的个数等等。只有经过了严格有效性验证的配置项才调用其对应的回调函数。

第三件事情就是调用其对应的回调函数进行处理。在处理函数内,根据实际的需要又可能再次调用函数ngx_conf_parse,如此反复直至所有配置信息都被处理完。

以上便是nginx配置文件的基本的解析流程。


参考资料:
高群凯.深入剖析nginx.北京:人民邮电出版社,2013

你可能感兴趣的:(Nginx)