Nginx极客时间:冲突的配置指令以谁为准

很多HTTP模块提供的指令,既可以出现在 http 、server、location、配置块中,当一个指令出现在多个配置块中的时候,有可能值是冲突的,应该以谁的值为准呢?


1. 指令的Context:(指令所能出现的位置)

Nginx极客时间:冲突的配置指令以谁为准
Nginx极客时间:冲突的配置指令以谁为准

“指令的Context” 是指 指令所能出现的上下文(配置块位置),例如上图中 “log_format” 这个指令的Context为“http”,则它只能出现在http{ } 配置块内,如果出现在其他配置块中,则Nginx在检查配置文件时就会报错,根本不能启动Nginx。


2. 指令的合并:(多个配置块出现相同指令时的合并策略)

指令分为两种:值指令 和 动作类指令。

值指令 用于存储配置项的值,例如 root、access_log、gzip 等,这类指令是可以合并的;

动作类指令 用于指定行为,例如 rewrite、proxy_pass 等,这类指令是不可以合并的。


存储值的指令继承规则:向上覆盖

存储值的指令的继承规则是:
(1)当 子配置不存在时,直接使用 父配置块;
(2)当 子配置存在时,直接覆盖父配置块。

举例:

server {
	listen		8080;
	root	/home/geek/nginx/html;
	access_log	logs/geek.access.log	main;
	
	location /test {
		root /home/geek/nginx/test;			#这个location块下的root的值与server{}配置块的值冲突,则直接覆盖父配置块的配置值
		access_log logs/access.test.log main;
	}

	location /dlib {
		alias dlib/;
	}

	location / {
		# 在这个location中并没有指定root指令的值,则直接继承父配置块server{} 在的root配置
	}
}


3. HTTP模块合并配置的实现:(从源码的层面分析指令的冲突覆盖策略)

有些第三方模块在实现时并没有严格遵循Nginx官方的指令冲突覆盖规则,又没有详细的说明文档,这是只能通过源码来判断某个具体的指令在哪个配置块下生效,判断依据有以下几种方法:

(1)指令允许出现在哪些块下?ngx_command_t

一个模块提供的所有配置指令全部都在 ngx_command_t 结构体构成的数组中可以找到,这个数组中的每个元素就是一条指令,其中 ngx_command_t 结构体中的 “type” 成员所指示的就是本条指令能出现在哪些配置块中:

ngx_command_t 结构体:

typedef struct ngx_command_s ngx_command_t {
	ngx_str_t		name;		//指令的名字

	ngx_uint_t		type;		//指示指令能出现在哪些配置块中、指令能接受多少个参数

	char			*(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);	//指令解析函数

	...
};

以 ngx_http_referer_commands 为例举例说明 type 字段的值:

static ngx_command_t ngx_http_referer_commands[] = {
	{
		ngx_string("valid_referers"),					//指令名为 valid_referers
		NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_1MORE,	//指令所能出现的位置:server、location
		ngx_http_valid_referers,
		NGX_HTTP_LOC_CONF_OFFSET,
		0,
		NULL
	},
	{

	},
	...
};

Nginx中 标志指令出现位置的宏:

//常用的三个:
#define	NGX_HTTP_MAIN_CONF	0x02000000		//http { }
#define NGX_HTTP_SRV_CONF	0x04000000		//server { }
#define NGX_HTTP_LOC_CONF	0x08000000		//location { }

//下面几个不是很常用:
#define NGX_HTTP_UPS_CONF	0x10000000		//upstream { }
#define NGX_HTTP_SIF_CONF	0x20000000
#define NGX_HTTP_LIF_CONF	0x40000000
#define NGX_HTTP_LMT_CONF	0x80000000

其中,type字段所取的另一个宏值 “NGX_CONF_1MORE” 表示本指令所能接受的参数个数,最多为8个:

//nginx指令可以接受的参数数量:
#define NGX_CONF_NOARGS	0x00000001
#define NGX_CONF_TAKE1	0x00000002
#define NGX_CONF_TAKE2	0x00000004
#define NGX_CONF_TAKE3	0x00000008
#define NGX_CONF_TAKE4	0x00000010
#define NGX_CONF_TAKE5	0x00000020
#define NGX_CONF_TAKE6	0x00000040
#define NGX_CONF_TAKE7	0x00000080

//表示最多只能接受8个参数
#define NGX_CONF_MAX_ARGS	8

#define NGX_CONF_1MORE	0x00000800	//可以携带 1个 或者 多个 参数
#define NGX_CONF_2MORE	0x00001000

(2)不同配置块间合并指令:ngx_http_module_t

如果多个配置块中都出现了相同的指令,如何判断某个配置块中的这条指令的取值:

ngx_module_t 结构体表示一个模块:

typedef struct ngx_module_s ngx_module_t;

struct ngx_module_s {
	void	*ctx;		//指向不同类型的模块实例
};

当此模块表示的是一个HTTP类型的模块,则 ngx_module_t->ctx 取值为 &ngx_http_module_t 。

ngx_http_module_t 中定义了 8个回调方法,用于 配置解析阶段 被框架代码调用:

//注意这8个回调方法的调用顺序并不是按照它们在结构体中的排列顺序
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);	//创建模块的main配置
	char		*(*init_main_conf)(ngx_conf_t *cf, void *conf);	//初始化模块的main配置

	void		*(*create_srv_conf)(ngx_conf_t *cf);	//创建模块的server配置
	char		*(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);	//初始化模块的server配置

	void 		*(*create_loc_conf)(ngx_conf_t *cf);	//创建模块的location配置
	char		*(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);	//初始化模块的location配置
} ngx_http_module_t;

所以,如果一个指令在 location 配置块内生效,那么它一定会定义 *(*create_loc_conf)*(*merge_loc_conf) 回调函数,用于创建模块的location配置,并将http和server配置块的配置合并到location配置块内。

以 ngx_http_referer_module 为例:

static ngx_http_module_t ngx_http_referer_module_ctx = {
	ngx_http_referer_add_variable,
	NULL,

	NULL,
	NULL,

	NULL,
	NULL,

	ngx_http_referer_create_conf,	//create_loc_conf
	ngx_http_referer_merge_conf		//merge_loc_conf
};

static char *ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child) {
	//parent就是父模块,如http、server配置块;child就是本配置块,即location配置块
	//从函数的实现方法中就可以看到此模块关于配置项的冲突配置策略
}
上一篇:SHELL编程概念&变量剖析


下一篇:【Linux网络编程】Nginx -- 模块开发(基本模块解析)