Nginx基础. 认识Nginx事件模块 (二)
来源:互联网 发布:spss mac版下载官网 编辑:程序博客网 时间:2024/06/04 19:42
对于事件模块, 定义此模块解析配置项的工作交给了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数组中看就可以了:
下面为了简洁, 就不贴代码了:
既然有这么多感兴趣的配置项, 那么其肯定会定义一个存储这些配置项的结构体:
具体成员含义也就不解释了, 与上面的感兴趣的配置项一一对应.
作为事件模块, ngx_event_core_module除了必要的create_conf和init_conf外, 并不需要实现一系列操作的函数, 因为它毕竟不是真正的事件驱动模块, 不负责TCP网络事件的驱动.
如下所示:
上面几个结构体都有了一定的了解后, 接下来看剩下的一个需要关注的结构体:
需要留意的地方在于, 此模块在init_module和init_process阶段都有一个回调函数.
那么init module类型的函数是什么时候执行的呢?
这段代码粗略的看是在配置完所有端口之后, fork出子进程之前执行的.
而对于ngx_event_process_init来说, 它是运行在fork出子进程之后执行的:
既然了解了这两个函数在什么地方执行 , 那么再了解其意义就显得更清楚些了.
ngx_event_module_init函数代码这里没有能力解读. 不过, 这里可以还是要粗略提一下此函数做的一番工作.
因为是多进程工作, 那么多进程之间想要通信就必须在fork出多个子进程之前做些准备, 比如申请共享内存, 实现accept mutex等
比如设置好每个进程的事件驱动机制的一些变量, 还有统计模块使用的一些原子性的统计变量......
相比于属于核心模块的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_tngx_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_create for (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;}
0 0
- Nginx基础. 认识Nginx事件模块 (二)
- Ngixn基础. 认识Nginx事件模块(一)
- nginx事件模块分析(二)
- Nginx基础. epoll事件驱动模块
- Nginx基础. Nginx模块上下文
- Nginx 事件模块
- Nginx事件模块
- nginx事件模块指令
- Nginx事件模块init
- Nginx事件模块小结
- nginx事件模块解析
- NGINX模块(二)
- Nginx模块开发---Nginx开发从入门到精通 读书笔记(二)---Nginx基础概念
- nginx事件模块分析(一)
- 【Nginx】epoll事件驱动模块
- nginx 事件模块简单剖析
- nginx事件模块执行过程
- nginx模块的认识(一)
- Android实战简易教程<四十三>(Shell Script 运行Command)
- LeetCode解题报告--Generate Parentheses
- 手机如何一键修改运营商的名称跟标识的快捷方法
- A股市场再度活跃,同步推炒股类app推荐
- iOS 蓝牙通讯
- Nginx基础. 认识Nginx事件模块 (二)
- Android实战简易教程<四十四>(Ripple Effect-为控件增加涟漪效果)
- Nginx基础. epoll事件驱动模块
- eclipse下搭建SSH整合环境(Struts2+Spring+Hibernate+maven)
- 为什么要使用onMeasure
- C++笔试题整理
- mac 配置maven环境变量
- [Cocos2d-x 升级IOS9错误解决方案]Invalid Bundle.iPad Multitasking support requires these orientations:XXX
- Android 判断当前线程是否是主线程的两种方法