Nginx基础. 认识Nginx事件模块 (二)

2024-06-22 19:48

本文主要是介绍Nginx基础. 认识Nginx事件模块 (二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对于事件模块, 定义此模块解析配置项的工作交给了 ngx_events_module, 对于事件驱动机制, 更多的则是在 ngx_event_core_module中进行的.
相比于属于核心模块的ngx_events_module, ngx_event_core_module则属于事件模块.
在ngx_modules.c文件中, ngx_event_core_module模块被放在了所有事件模块的最前面. 因为此模块会先于其他事件模块进行, 完成它选择事件驱动机制的任务.
那么ngx_event_core_module对哪些配置项感兴趣呢? 这只要从其定义的 ngx_commands_t数组中看就可以了:
下面为了简洁, 就不贴代码了:
worker_connections:定义连接池的大小(上一篇文章有讲), 也就是每个worker进程中支持的TCP的最大连接数
connections:与上面的含义一样
use:确定选择哪个事件模块作为事件驱动机制
multi_accept:对应于事件结构体ngx_event_s中的available成员, 意味着在接收到一个新连接事件时, 调用accept尽可能多的接收连接
accept_mutex:是否使用负载均衡锁(默认开启)
accept_mutex_delay:启用负载均衡锁后, 延迟一定时间后再试图处理新的连接事件
debug_connection需要对来自指定IP的TCP连接打印debug级别的调试日志

既然有这么多感兴趣的配置项, 那么其肯定会定义一个存储这些配置项的结构体:
具体成员含义也就不解释了, 与上面的感兴趣的配置项一一对应.
typedef struct {ngx_uint_t    connections;//选用的事件模块在所有事件模块中的序号. 即该驱动模块的ctx_index成员ngx_uint_t    use;ngx_flag_t    multi_accept;ngx_flag_t    accept_mutex;ngx_msec_t    accept_mutex_delay;u_char       *name;#if (NGX_DEBUG)ngx_array_t   debug_connection;
#endif
} ngx_event_conf_t;


作为事件模块, ngx_event_core_module除了必要的create_conf和init_conf外, 并不需要实现一系列操作的函数, 因为它毕竟不是真正的事件驱动模块, 不负责TCP网络事件的驱动.
如下所示:

static ngx_str_t  event_core_name = ngx_string("event_core");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 */{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};


上面几个结构体都有了一定的了解后, 接下来看剩下的一个需要关注的结构体:
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 */ngx_event_module_init,                 /* init module */ngx_event_process_init,                /* init process */NULL,                                  /* init thread */NULL,                                  /* exit thread */NULL,                                  /* exit process */NULL,                                  /* exit master */NGX_MODULE_V1_PADDING
};

需要留意的地方在于, 此模块在init_module和init_process阶段都有一个回调函数.
那么init module类型的函数是什么时候执行的呢?
     //下面是截取的ngx_cycle.c中的一段for (i = 0; ngx_modules[i]; i++) {if (ngx_modules[i]->init_module) {if (ngx_modules[i]->init_module(cycle) != NGX_OK) {/* fatal */exit(1);}}}

这段代码粗略的看是在配置完所有端口之后, fork出子进程之前执行的.
而对于ngx_event_process_init来说, 它是运行在fork出子进程之后执行的:
     //截取自ngx_worker_process_init函数for (i = 0; ngx_modules[i]; i++) {if (ngx_modules[i]->init_process) {if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {/* fatal */exit(2);}}}



既然了解了这两个函数在什么地方执行 , 那么再了解其意义就显得更清楚些了.
ngx_event_module_init函数代码这里没有能力解读. 不过, 这里可以还是要粗略提一下此函数做的一番工作.
        因为是多进程工作, 那么多进程之间想要通信就必须在fork出多个子进程之前做些准备, 比如申请共享内存, 实现accept mutex等
        比如设置好每个进程的事件驱动机制的一些变量, 还有统计模块使用的一些原子性的统计变量......

ngx_event_process_init函数则做了许多工作
static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{ngx_uint_t           m, i;ngx_event_t         *rev, *wev;ngx_listening_t     *ls;ngx_connection_t    *c, *next, *old;ngx_core_conf_t     *ccf;ngx_event_conf_t    *ecf;ngx_event_module_t  *module;//得到核心模块的存储配置项结构体ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);//得到ngx_event_core_module模块的存储配置项结构体ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);//如果master进程是存在的(不存在master进程表示单进程工作方式, 不需要accept锁)//只有一个worker进程, 不需要accept锁 (所以即使在配置文件中声明要使用accept锁, 如果进程数量不大于1, 依旧不用accept锁)//成功创建了accept锁(在ngx_event_module_init中)if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {ngx_use_accept_mutex = 1;             //使用accept锁ngx_accept_mutex_held = 0;            //当前进程不持有锁ngx_accept_mutex_delay = ecf->accept_mutex_delay;  //争抢accept锁失败后,下次参与争抢的间隔} else {ngx_use_accept_mutex = 0;}//初始化两个post队列. 关于两个post队列, 以后会讲到ngx_queue_init(&ngx_posted_accept_events);ngx_queue_init(&ngx_posted_events);//初始化计数器, 这里会创建一颗红黑树, 来维护众多计时器. 以后会讲到if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {return NGX_ERROR;}//找到Nginx使用的事件驱动机制模块, 并对事件驱动机制进行初始化工作//对于epoll来说, 此函数有调用epoll_createfor (m = 0; ngx_modules[m]; m++) {...  //找到使用的事件驱动模块if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) exit(2);break;}#if !(NGX_WIN32)//如果配置文件中有配置项ngx_timer_resolution, 那么表明需要控制时间精度//这里我们来看一下如何控制时间精度if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {struct sigaction  sa;struct itimerval  itv;ngx_memzero(&sa, sizeof(struct sigaction));sa.sa_handler = ngx_timer_signal_handler;sigemptyset(&sa.sa_mask);//可以发现, 这里设置了信号处理函数, 针对的信号是SIGALRM, 处理函数是ngx_timer_signal_handler//跳转到ngx_timer_signal_handler函数, 发现其作用就是使ngx_event_timer_alarm置1, 置1有什么用呢?//该变量置1表示需要更新时间. 在事件驱动机制实现的事件模块接口ngx_event_module_t中的ngx_event_actions_t成员中的process_events中,//当ngx_event_timer_alarm为1时, 都会调用ngx_time_update方法更新系统时间. 下面对定时器还会有更详细的分析if (sigaction(SIGALRM, &sa, NULL) == -1) {ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,"sigaction(SIGALRM) failed");return NGX_ERROR;}itv.it_interval.tv_sec = ngx_timer_resolution / 1000;itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;itv.it_value.tv_sec = ngx_timer_resolution / 1000;itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;//引起SIGALRM的函数是setitimer函数. 此函数用于间隔性的引起SIGALRM信号if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,"setitimer() failed");}}//下面是预分配文件结构体. 暂时不作理解if (ngx_event_flags & NGX_USE_FD_EVENT) {...}#endif//预分配连接池cycle->connections =ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);c = cycle->connections;//预分配读事件池cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,cycle->log);rev = cycle->read_events;for (i = 0; i < cycle->connection_n; i++) {rev[i].closed = 1;rev[i].instance = 1;}//预分配写事件池cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,cycle->log);wev = cycle->write_events;for (i = 0; i < cycle->connection_n; i++) {wev[i].closed = 1;}i = cycle->connection_n;next = NULL;//根据下标将所有读事件和写事件 与 连接联系起来//与此同时, 构建空闲连接链表do {i--;c[i].data = next;c[i].read = &cycle->read_events[i];c[i].write = &cycle->write_events[i];c[i].fd = (ngx_socket_t) -1;next = &c[i];} while (i);cycle->free_connections = next;cycle->free_connection_n = cycle->connection_n;/* for each listening socket *///为每个ngx_listening_t监听对象中的onnection分配连接.ls = cycle->listening.elts;for (i = 0; i < cycle->listening.nelts; i++) {c = ngx_get_connection(ls[i].fd, cycle->log);if (c == NULL) {return NGX_ERROR;}c->log = &ls[i].log;c->listening = &ls[i];ls[i].connection = c;rev = c->read;rev->log = c->log;rev->accept = 1;#if (NGX_HAVE_DEFERRED_ACCEPT)rev->deferred_accept = ls[i].deferred_accept;
#endif...//设置该连接对应的读事件为ngx_event_accept//即该监听端口有新连接时将调用ngx_event_accept处理函数建立连接rev->handler = ngx_event_accept;//既然是多进程, 那么如果多个进程同时监听一个套接字, 有可能引发"惊群"现象//所以, 为了避免惊群, 使用了accept锁, 且在进程没有获得锁之前, 不把该监听事件放到epoll中.if (ngx_use_accept_mutex) {continue;}if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {if (ngx_add_conn(c) == NGX_ERROR) {return NGX_ERROR;}} else {if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {return NGX_ERROR;}}}return NGX_OK;
}


这篇关于Nginx基础. 认识Nginx事件模块 (二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1085246

相关文章

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

Vim使用基础篇

本文内容大部分来自 vimtutor,自带的教程的总结。在终端输入vimtutor 即可进入教程。 先总结一下,然后再分别介绍正常模式,插入模式,和可视模式三种模式下的命令。 目录 看完以后的汇总 1.正常模式(Normal模式) 1.移动光标 2.删除 3.【:】输入符 4.撤销 5.替换 6.重复命令【. ; ,】 7.复制粘贴 8.缩进 2.插入模式 INSERT

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

ps基础入门

1.基础      1.1新建文件      1.2创建指定形状      1.4移动工具          1.41移动画布中的任意元素          1.42移动画布          1.43修改画布大小          1.44修改图像大小      1.5框选工具      1.6矩形工具      1.7图层          1.71图层颜色修改          1

iptables(7)扩展模块state

简介         前面文章我们已经介绍了一些扩展模块,如iprange、string、time、connlimit、limit,还有扩展匹配条件如--tcp-flags、icmp。这篇文章我们介绍state扩展模块  state          在 iptables 的上下文中,--state 选项并不是直接关联于一个扩展模块,而是与 iptables 的 state 匹配机制相关,特

vue+elementui分页输入框回车与页面中@keyup.enter事件冲突解决

解决这个问题的思路只要判断事件源是哪个就好。el分页的回车触发事件是在按下时,抬起并不会再触发。而keyup.enter事件是在抬起时触发。 so,找不到分页的回车事件那就拿keyup.enter事件搞事情。只要判断这个抬起事件的$event中的锚点样式判断不等于分页特有的样式就可以了 @keyup.enter="allKeyup($event)" //页面上的//js中allKeyup(e

python 在pycharm下能导入外面的模块,到terminal下就不能导入

项目结构如下,在ic2ctw.py 中导入util,在pycharm下不报错,但是到terminal下运行报错  File "deal_data/ic2ctw.py", line 3, in <module>     import util 解决方案: 暂时方案:在终端下:export PYTHONPATH=/Users/fujingling/PycharmProjects/PSENe

[FPGA][基础模块]跨时钟域传播脉冲信号

clk_a 周期为10ns clk_b 周期为34ns 代码: module pulse(input clk_a,input clk_b,input signal_a,output reg signal_b);reg [4:0] signal_a_widen_maker = 0;reg signal_a_widen;always @(posedge clk_a)if(signal_a)

00 - React 基础

1. React 基础 安装react指令 可参考: 官网官网使用教程 如: npx create-react-app 项目名如:npx create-react-app react-redux-pro JSX JSX 是一种 JavaScript 的语法扩展,类似于 XML 或 HTML,允许我们在 JavaScript 代码中编写 HTML。 const element =

AI赋能天气:微软研究院发布首个大规模大气基础模型Aurora

编者按:气候变化日益加剧,高温、洪水、干旱,频率和强度不断增加的全球极端天气给整个人类社会都带来了难以估计的影响。这给现有的天气预测模型提出了更高的要求——这些模型要更准确地预测极端天气变化,为政府、企业和公众提供更可靠的信息,以便做出及时的准备和响应。为了应对这一挑战,微软研究院开发了首个大规模大气基础模型 Aurora,其超高的预测准确率、效率及计算速度,实现了目前最先进天气预测系统性能的显著