前言
本小节主要是整理一下前几节分析的nginx的核心模块的ngx_events_module以及事件模块,关于事件模块什么时候初始化以及事件的处理等,因此不会涉及到太多具体的代码,主要是把握事件模块的整体。
配置项结构体的创建及赋值
在使用一个模块的功能之前,我们需要先根据配置文件来定制该模块的功能,对于事件模块来说,有选取哪种事件模型,以及单个进程的最大连接数等配置指令。
在ngx_init_cycle中第一次调用ngx_conf_parse,遇到了event {}配置项时,会回调ngx_events_module中ngx_commands_t结构体的set成员指向的函数。而ngx_events_module就会通过set函数指针调用ngx_events_block函数,它所做的工作大致如下:
- 申请存储配置项结构体指针的数组以及指向该数组的指针的空间
- 调用所有事件模块的
create_conf方法(它们会各自申请存储配置项的结构体的空间),将指向这些结构体的指针收集起来 - 解析
nginx.conf文件,让所有事件模块解析自己感兴趣的配置项并存储到结构体中(回调ngx_commands_t结构体的set成员指向的函数) - 最后调用所有事件模块的
init_conf方法,init_conf方法的主要工作就是将没有设置初值的配置项结构体成员设置为默认值
对于ngx_event_core_module来说,它关注的配置有连接池的大小(worker_connections、connections)、选择哪一种事件驱动机制(use)、是否一次性尽可能的接收连接(multi_accept)、是否使用负载均衡锁(accept_mutex)、用负载均衡锁后,延迟多少毫秒后再试图处理新连接(accept_mutex_delay)等
对于ngx_epoll_module来说,它关注的配置有监听的事件个数,即传入epoll_wait的第三个参数(epoll_events)、初始分配的异步I/O事件个数 worker_aio_requests
初始化工作
经过了以上步骤,ngx_init_cycle接着遍历ngx_modules数组,调用各模块的init_module方法,根据配置项结构体中的成员做一些初始化工作。对于ngx_event_core_module模块来说,该函数会根据其配置项结构体的内容初始化一些用于统计变量(与事件模块关系不是很大),而ngx_epoll_module则没有实现该方法。
接下来的工作,就可以开始分不同进程进行了。还记得nginx通过什么样的步骤来开启的子进程的吗?首先配置文件中设置的worker_processes配置指令如果为1,则nginx以单进程模式运行,若大与1,则以多进程模式运行。
多进程模式下,nginx会调用ngx_master_process_cycle函数,即master进程的工作循环。在master进程的工作循环中,它会先设置一些信号的捕捉函数,别忘了master进程靠各种标志位来选择进行什么样的工作。接着就会调用ngx_start_worker_processes函数启动worker_processes个子进程。在ngx_start_worker_processes中,调用的核心函数是ngx_spawn_process,其中fork了进程之后,会调用ngx_worker_process_cycle,即子进程的工作循环。
在子进程的工作循环中,第一件事就是调用ngx_worker_process_init,它会调用所有模块的init_process函数。对于事件模块中的ngx_event_core_module模块来说,它对应的init_process主要做了以下工作:
- 初始化定时器
- 调用配置中指定的事件驱动模块的
init方法 - 若设置了
timer_resolution,则定时调用ngx_timer_signal_handler,控制时间精度 - 预分配连接池(
cycle->connections) - 预分配读事件(
cycle->read_events) - 预分配写事件(
cycle->write_events) - 将对应的读、写事件放置到对应的
connections连接中 - 初始化连接池,当前所有连接均为空闲连接,因此
cycle->free_connections指向连接池首部 - 设置监听套接字上读事件的处理方法为
ngx_event_accept,即建立新连接 - 将监听套接字的读事件添加到事件驱动模块中(若打开了
accept_mutex锁不会执行这一步)
处理事件
接着子进程正式开始工作,而其工作的核心函数就是ngx_process_events_and_timers,若开启了accept_mutex并且当前进程的负载还没有达到总连接的7/8,会让进程去获取accept_mutex锁,获取到该锁的进程可以处理新连接,没有获取到的只能调用epoll_wait处理已有的连接的事件,不过过若在epoll_wait上阻塞了accept_mutex_delay毫秒之后,又可以再次尝试去获取锁。调用epoll_wait并处理事件的函数是ngx_process_events,对应epoll模块中是ngx_epoll_process_events。
获取到了accept_mutex锁的进程会将就绪的事件放入对应的post事件队列中延后处理,因为如果还是在ngx_process_events函数中调用处理函数handler,会导致进程长时间占有accept_mutex锁得不到释放,导致其他进程获取不到该锁。因此ngx_process_events还负责将设置了NGX_POST_EVENTS标志位的事件加入post事件队列,注意post事件队列有两个,一个是建立新连接的事件的队列,另一个是其他普通的读/写事件的队列。在ngx_process_events_and_timers中先将post事件队列中建立新连接的事件处理了,然后释放锁,接着再处理post事件队列的其他事件。
小结
本小节梳理了一下从main函数开始再到事件模块处理事件的大致过程,这对把握nginx整个框架很有帮助,毕竟http、mail这些模块其实都属于事件消费模块,需要围绕着事件模块展开。

本文详细介绍了Nginx事件模块的配置项结构体创建、初始化工作流程以及事件处理过程。包括配置项的解析与赋值、事件驱动模块的选择、连接池与事件的预分配等内容。
1288

被折叠的 条评论
为什么被折叠?



