nginx事件模块结构体详解
事件模块是nginx的核心模块之一,nginx中客户端请求的处理和命令行指令的执行都是基于事件模块进行驱动的。因此,掌握事件模块的实现原理对于我们理解nginx整体架构有非常重要的意义。本文首先会讲解事件模块相关的几个模块定义及其执行流程进行讲解,其源码的讲解将会在后面的文章中进行。
nginx的事件核心模块主要有两个:ngx_events_module
和ngx_event_core_module
。这两个模块的主要区别在于,ngx_events_module
的类型为NGX_CORE_MODULE
,其本质上虽然是核心模块类型,但是却是事件模块的一个驱动点,其解析的配置项为events {}
,并且会创建用于存储事件模块相关配置的结构体;而ngx_event_core_module
的类型则是NGX_EVENT_MODULE
,这个模块的配置对象中会存储事件模块的基础配置对象,其主要作用是解析events{}
配置块中的子配置项。下面我们就来看一下这两个模块的基础配置。
1. ngx_events_module
如下是ngx_events_module
模块的基础配置:
ngx_module_t ngx_events_module = { NGX_MODULE_V1, &ngx_events_module_ctx, /* module context */ ngx_events_commands, /* module directives */ NGX_CORE_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 }; static ngx_core_module_t ngx_events_module_ctx = { ngx_string("events"), NULL, ngx_event_init_conf }; static ngx_command_t ngx_events_commands[] = { {ngx_string("events"), NGX_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_NOARGS, ngx_events_block, 0, 0, NULL}, ngx_null_command };
在ngx_events_module
模块的定义中,其module context指向的是ngx_events_module_ctx
,也即第二个结构体的配置,而module directives指向的则是ngx_events_commands,也即第三个结构体的定义。可以看到,ngx_events_commands
中只定义了一个events
配置项,而这个配置项的类型为NGX_CONF_BLOCK
,也就是说其是一个配置块的类型,这里我们就理解了,这个command对应的就是我们在nginx.conf中使用的events {}
配置块,而这个配置块的解析则是通过ngx_events_block()
方法进行的。
我们知道,在nginx的核心配置对象ngx_cycle_t中的conf_ctx数组中,每个模块在数组对应的位置都会有一个配置对象,同理,这里的核心模块也会有一个配置对象,但是上面的第二个结构体ngx_events_module_ctx
中的第二个属性值为NULL,也就是说这里的事件模块的定义中是没有创建配置对象的方法的,但是却有初始化配置对象的方法,也即第三个属性值ngx_event_init_conf()
方法。那么这里事件模块的配置对象是在哪里创建的呢?其实其就是在解析配置对象的时候进行的,也即在执行解析events {}
配置块的ngx_events_block()
方法中进行的。该方法本质上只是创建了一个指针数组,然后将其赋值给nginx核心和值对象的ngx_cycle_t的conf_ctx的对应位置。
2. ngx_event_core_module
在介绍ngx_event_core_module
模块之前,我们首先需要讲解一下事件模块的接口定义:
typedef struct { // 事件模块的名称 ngx_str_t *name; // 在解析配置项前,这个回调方法用于创建存储配置项参数的结构体 void *(*create_conf)(ngx_cycle_t *cycle); // 在解析配置项完成后,init_conf()方法会被调用,用以综合处理当前事件模块感兴趣的全部配置项 char *(*init_conf)(ngx_cycle_t *cycle, void *conf); // 对于事件驱动机制,每个事件模块需要实现的10个抽象方法 ngx_event_actions_t actions; } ngx_event_module_t; typedef struct { // 添加事件方法,它负责把一个感兴趣的事件添加到操作系统提供的事件驱动机制(epoll、kqueue等)中, // 这样,在事件发生后,将可以在调用下面的process_events时获取这个事件 ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); // 删除事件方法,它把一个已经存在于事件驱动机制中的事件移除,这样以后即使这个事件发生, // 调用process_events()方法时也无法再获取这个事件 ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); // 启用一个事件,目前事件框架不会调用这个方法,大部分事件驱动模块对于该方法的实现都是 // 与上面的add()方法完全一致的 ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); // 禁用一个事件,目前事件框架不会调用这个方法,大部分事件驱动模块对于该方法的实现都是 // 与上面的del()方法完全一致的 ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); // 向事件驱动机制中添加一个新的连接,这意味着连接上的读写事件都添加到事件驱动机制中了 ngx_int_t (*add_conn)(ngx_connection_t *c); // 从事件驱动机制中移除一个连接的读写事件 ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags); ngx_int_t (*notify)(ngx_event_handler_pt handler); // 在正常的工作循环中,将通过调用process_events()方法来处理事件。 // 这个方法仅在ngx_process_events_and_timers()方法中调用,它是处理、分发事件的核心 ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); // 初始化事件驱动模块的方法 ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer); // 退出事件驱动模块前调用的方法 void (*done)(ngx_cycle_t *cycle); } ngx_event_actions_t;
nginx的事件模块主要有两个配置结构体:ngx_event_module_t
和ngx_event_actions_t
两个。从上面的定义中可以看出,ngx_event_module_t
结构体是引用了ngx_event_actions_t
的。ngx_event_module_t
的作用主要是创建和初始化当前模块所需要的配置结构体,而ngx_event_actions_t
则主要定义了当前事件模块处理各个事件的方式,这个接口典型的实现接口是nginx定义的各个事件模型,比如epoll的ngx_epoll_module_ctx.actions
和kqueue的ngx_kqueue_module_ctx.actions
。从这里就可以看出,事件模块的定义抽象了各个处理事件的模型的相关处理方法。
对于事件模块而言,其有一个模块是用于存储事件相关的基础配置的,即ngx_event_core_module
,虽然实现了ngx_event_module_t
接口,但是其不进行具体的事件处理。如下是ngx_event_core_module
模块的定义:
ngx_module_t ngx_event_core_module = { NGX_MODULE_V1, &ngx_event_core_module_ctx, /* module context */ ngx_event_core_commands, /* module directives */ NGX_EVENT_MODULE, /* module type */ NULL, /* init master */ // 该方法主要是在master进程启动的过程中调用的,用于初始化时间模块 ngx_event_module_init, /* init module */ // 该方法是在各个worker进程启动之后调用的 ngx_event_process_init, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static ngx_event_module_t ngx_event_core_module_ctx = { &event_core_name, ngx_event_core_create_conf, /* create configuration */ ngx_event_core_init_conf, /* init configuration */ // ngx_event_core_module_ctx并不直接负责TCP网络事件的驱动, // 因而这里的ngx_event_actions_t中的方法都为NULL {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL} }; static ngx_command_t ngx_event_core_commands[] = { // 连接池的大小,也即每个worker进程中支持的TCP最大连接数,它与connections配置项的意义是重复的 {ngx_string("worker_connections"), NGX_EVENT_CONF | NGX_CONF_TAKE1, ngx_event_connections, 0, 0, NULL}, // 确定选择哪一个事件模块作为事件驱动机制 {ngx_string("use"), NGX_EVENT_CONF | NGX_CONF_TAKE1, ngx_event_use, 0, 0, NULL}, // 对应于ngx_event_s中的available属性,对于epoll事件驱动模式来说,意味着在接收到一个新连接事件时, // 调用accept以尽可能多地接收连接 {ngx_string("multi_accept"), NGX_EVENT_CONF | NGX_CONF_FLAG, ngx_conf_set_flag_slot, 0, offsetof(ngx_event_conf_t, multi_accept), NULL}, // 确定是否使用accept_mutex负载均衡锁,默认为开启 {ngx_string("accept_mutex"), NGX_EVENT_CONF | NGX_CONF_FLAG, ngx_conf_set_flag_slot, 0, offsetof(ngx_event_conf_t, accept_mutex), NULL}, // 启用accept_mutex负载均衡锁后,延迟accept_mutex_delay毫秒后再试图处理新连接事件 {ngx_string("accept_mutex_delay"), NGX_EVENT_CONF | NGX_CONF_TAKE1, ngx_conf_set_msec_slot, 0, offsetof(ngx_event_conf_t, accept_mutex_delay), NULL}, // 需要对来自指定IP的TCP连接打印debug级别的调试日志 {ngx_string("debug_connection"), NGX_EVENT_CONF | NGX_CONF_TAKE1, ngx_event_debug_connection, 0, 0, NULL}, ngx_null_command };
在事件模块的定义中,module context指向的是一个ngx_event_module_t
结构体,这里的ngx_event_core_module
的module context指向的就是第二个结构体定义的ngx_event_core_module_ctx
,而ngx_event_core_module_ctx
中则定义了当前核心模块创建配置对象和初始化配置对象的方法,可以看到,其actions属性中的值全部为NULL,这是因为该模块并不负责处理具体的事件处理方案,而是负责核心结构体的创建和初始化,nginx也会保证这个模块在所有的事件模块中最先被调用,其余各个事件模块也可以引用该模块所存储的基础配置数据。
在ngx_event_core_module
中第三个属性ngx_event_core_commands
指向的是上面的第三个结构体,这个结构体中定义了当前事件模块所能使用的各个配置项的基本配置以及解析这些配置项的方法。
这里我们需要着重强调ngx_event_core_module
中的第六个和第七个属性,这两个属性指向的是都是某个方法,第六个属性init module的主要是在nginx启动过程中解析完nginx.conf配置文件之后执行,其作用是对当前模块进行初始化的工作,而第七个属性init process主要是在nginx启动worker进程之后worker进程开始执行主循环之前调用的,其作用是进行worker进程执行前的初始化工作。
3. 模块方法的执行流程
通过上面的介绍我们大致了解了定义事件模块的两个核心模块的主要方法及其作用,这里则主要是对这些方法的执行流程进行讲解,如下是其流程示意图:
对于上面的,这里需要对其各个步骤的功能进行说明:
- 解析nginx.conf文件,当遇到events配置项时,就使用ngx_evetns_block()方法对其进行解析;
- 创建用于存储各个事件模块存储配置项的结构体的数组;
- 采用递归的方式解析events配置块中的子配置项;
- 依次检查事件核心模块的配置项,如果其没有赋值,则对其赋一个默认值;
- 检查是否创建了存储事件模块配置项的数组,该检查的主要目的是判断核心模块是否成功初始化了;
- 主要是通过解析得到的配置项,设置诸如时间定时器的执行频率、可打开的文件句柄限制和初始化记录统计数据的属性;
- 在worker进程中调用,用于初始化worker进程运行所需要的环境,比如初始化事件队列、初始化事件模型、添加时间更新的定时任务等;
4. 小结
本文首先对nginx事件模块的核心结构体的基本配置和作用进行了详细讲解,然后讲解了这些配置方法的调用流程,并且讲解了各个流程方法的主要作用,下一篇文章将会详细讲解这些流程的具体实现原理。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
《吐血整理》顶级大佬学习方法
你知道的越多,你不知道的越多 点赞再看,养成习惯 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试点思维导图,也整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。 前言 很多次小伙伴问到学习方法,我也很想写这样的一篇文章来跟大家讨论下关于学习方法这件事情。 其实学习方法这个事情,我没啥发言权,因为我自己本身都是没啥方法可言的,就瞎折腾那种,但是大家想看这样的一篇文章,我就结合身边比较优秀的仔,那就聊一下这个事情吧。 写这个文章的的时候我构思了很多种写法,因为考虑我读者很多大学生,也有很多毕业的仔,想照顾所有的群体,那我整个文章会从读书时候的一些学习方法,还有就是工作之后的工作学习方法去写,这个思路应该也ok。 正文 回忆 在大学期间,我自己本身是没啥学习方法的,就是一头莽进书本视频里面,大一打了一年LOL,大二迷途知返(主要是被学霸的哥哥姐姐DISS了)自己也发现玩下去不是办法,于是去图书馆肝了一年,这个时候把专业知识和很多东西都补上来了。 实践是检验真理的唯一标准,这句话大家一定要记住,多实践...
- 下一篇
突破CRUD | 简单优雅的代码生成工具诞生记(万字长文慎入)
0、学习本文你或许可以收获 1、一个需求从产生、分析到解决的全过程思考 2、简单的面向对象分析实践 3、UML类图实践 4、设计模式的实践应用 5、最后收获一款还算不错的代码生成工具实现思路和源代码 本文将从上面第一点提到的全过程需求->目标->思路->设计->实现->总结逐一展开。 本文为了尽量还原真实场景下如何从无到有实现一个需求,所以全文会假设大家都不动代码生成这个东西。是需要从零开始研究的。 下面开始正文。 1、先看需求 某日,风小南(年轻时的自己)代码写的正酣,技术经理突然走了过来,拍拍了风小南肩膀。 “小南啊,你看咱这个系统里面这么多增删改查的功能,虽然拷贝复制可以,但是效率还是不够高”。 风小南心想 "都CV模式了效率还不高,你咋不上天呢 ?"。 紧接着,技术经理说道 你看,现在这个用户管理模块做完了,我做其他角色、机构管理时,代码的规范、基本的增删改查结构是一样的对吧,确实可以快速复制实现其他两个功能,但是这中间有几个问题仍然影响效率 第一:文件重命名问题,一个基本的模块从前台到后台大概7-10个左右的代码文件,在复制的同时均需要重新命名...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8编译安装MySQL8.0.19
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS7,8上快速安装Gitea,搭建Git服务器