后浪笔记一零二四

nginx模块

模块配置结构

nginx配置信息分为几个作用域,main, server, 以及location。同样的,每个模块提供的配置指令也可以出现在这几个作用域中。对于这三个作用域的配置信息,每个模块要按需定义不同的数据结构去存储,需要几个就定义几个。

对于模块配置信息的定义,命名习惯是ngx_http_<module name>_(main/srv/loc)_conf_t, 例如:

1
2
3
4
typedef struct {
	ngx_str_t hello_string;
	ngx_int_t hello_counter;
}ngx_http_hello_loc_conf_t;

模块配置指令

一个模块的配置指令是定义在一个静态数组中的,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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
}

上面的例子,定义了两个配置指令:

  1. hello_string: 可以接受一个参数,或者是没有参数
  2. hello_counter: 接受一个NGX_CONF_FLAG类型的参数

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

1
2
3
4
5
6
7
8
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。如果写上面三个属性在一起,比较麻烦,就是用下面这些配置:

    • 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: 配置指令接受至少2个参数
    • 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: 该配置指令可以出现在任意配置级别上
    • 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: 该参数里面保存从配置文件读取到的原始字符串以及相关的一些信息。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函数:
      • 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_unit_t类型
      • ngx_conf_set_bigmask_slot: 读取参数的值,并将这些参数的值以bit位的形式参数。例如:HttpDavModule模式的dav_methods指令
  • conf: 该字段被NGX_HTTP_MODULE类型模块所用(我们编写的基本上都是NGX_HTTP_MODULE,只有一些nginx核心模块是非NGX_HTTP_MODULE),该字段指定当前配置项存储的内存位置。实际上是使用哪个内存池的问题。因为http模块对所有http模块所要保存的配置信息,划分了main,server和location三个地方进行存储,每个地方都有一个内存池用来分配存储这些信息的内存。这里可能的值为NGX_HTTP_MAIN_CONF_OFFSETNGX_HTTP_SRV_CONF_OFFSETNGX_HTTP_LOC_CONF_OFFSET。当然也可以直接置为0,就是NGX_HTTP_MAIN_CONF_OFFSET

  • offset: 指定该配置项值的精确存放位置,一般指定为某一个结构体变量的字段偏移。因为对于配置信息的存储,一般我们都是定义个结构体来存储的。如果我们定义了一个结构体A,该项配置的值需要存储到该结构体的b字段,那么就设置为offsetof(A,b)

  • post: 该字段存储一个指针。可以指向任何一个在读取配置过程中需要的数据,以便于进行配置读取的处理。大多数时候,都不需要,所以简单地设为0即可。

模块上下文结构

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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。
  • int_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定义了一系列的宏定义来代表各种配置所对应数据类型的未初始化值,如下:

1
2
3
4
5
#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

模块的定义

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
typedev 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

再看一下hello模块的模块定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
ngx_module_t ngx_http_hello_module = {
	NGX_MODULE_V1,
	&ngx_http_hello_module_ctx,     /* module context */
	ngx_http_hello_commands,        /* module directives */
	NGX_HTTP_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
};

模块可以提供一些回调函数给nginx,当nginx在创建进程线程或者结束进程线程时进行调用。但大多数模块在这些时刻并不需要做什么,所以都简单赋值给NULL。

handler模块的基本结构

handler模块还必须提供一个真正的处理函数,这个函数负责对来自客户端请求的真正处理。这个函数的处理,既可以选择自己直接生成的内容,也可以选择拒绝处理,由后续的handler去进行处理,或者是选择丢给后续的filer进行处理。

typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);

r是http请求。里面包含请求所有的信息,这里不详细说明了,可以参考别的章节的介绍。该函数处理成功返回NGX_OK,处理发生错误返回NGX_ERROR,拒绝处理(留给后续的handler进行处理)返回NGX_DECLINE。返回NGX_OK也就代表给客户端的响应已经生成好了,否则返回NGX_ERROR就发生错误了。

handler模块的挂载


专题:

本文发表于 2022-01-01,最后修改于 2022-01-01。

本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。


上一篇 « gnu 下一篇 » 自己动手写session

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image