Nginx:事件模块
参考资料<深入理解Nginx>
根据不同的系统内核,Nginx会使用不同的事件驱动机制,本次描述的场景是使用epoll来驱动事件的处理。
epoll的使用方法
1.int epoll_create(int size);
epoll_create返回一个句柄,之后epoll的使用将依靠这个句柄来标识。参数size只是告诉epoll所要处理的大致事件数目,一些内核版本的实现中,这个参数没有任何意义。
2.int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
epoll_ctl向epoll对象中添加、修改或者删除感兴趣的事件,返回0表示成功,否则返回-1。
参数epfd是epoll_create方法返回的句柄,而op参数的意义如下表
参数fd是待检测的连接套接字,第四个参数是在告诉epoll对什么样的事件感兴趣,它使用的epoll_event结构体定义如下
struct epoll_event {
_uint32_t events;
epoll_data_t data;
};
events取值如下表
而data成员是一个epoll_data联合
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
这个data成员还与具体的使用方式相关。例如,ngx_epoll_module模块使用了给ptr成员,作为指向ngx_connection_t连接的指针。
3.int epoll_wait(int fd,struct epoll_event *events,int maxevents,int timeout);
第一个参数epfd是epoll的描述符。第二个参数events则是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中,
第三个参数表示本次可以返回的最大事件数目,第四个参数表示在没有检测到时间发生时最多等待的时间。
ngx_epoll_module模块
该模块使用如下的上下文结构
ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */ {
ngx_epoll_add_event, /* add an event */
ngx_epoll_del_event, /* delete an event */
ngx_epoll_add_event, /* enable an event */
ngx_epoll_del_event, /* disable an event */
ngx_epoll_add_connection, /* add an connection */
ngx_epoll_del_connection, /* delete an connection */
NULL, /* process the changes */
ngx_epoll_process_events, /* process the events */
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};
在Nginx的启动过程中。ngx_epoll_init方法将会被调用,它主要做了两件事情:
1.调用epoll_create方法创建epoll对象。
2.创建event_list数组,用于进行epoll_wait调用时传递内核态的事件。
ngx_epoll_add_event通过调用epoll_ctl向epoll中添加或者删除事件。可以通过这个函数的部分源码来了解一下该过程
static ngx_int_t
ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
int op;
uint32_t events, prev;
ngx_event_t *e;
ngx_connection_t *c;
struct epoll_event ee; //每个事件的data成员都存放着其对应的ngx_connection_t连接
c = ev->data; //确定当前时间是读事件还是写事件
events = (uint32_t) event; ...
//根据active标志位确定是否为活跃事件,以决定到底是修改还是添加事件
if (e->active) {
op = EPOLL_CTL_MOD;
events |= prev; } else {
op = EPOLL_CTL_ADD;
} ee.events = events | (uint32_t) flags;
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); //调用epoll_ctl方法向epoll中添加事件
if (epoll_ctl(ep, op, c->fd, &ee) == -) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
} //标识为活跃事件
ev->active = ; return NGX_OK;
}
同理,ngx_epoll_del_event方法也通过类似的方式删除事件。
对于ngx_epoll_add_connection和ngx_epoll_del_connection方法,也是调用epoll_ctl进行处理,只是每一个连接都对应读/写事件。
ngx_epoll_process_events方法则调用epoll_wait来获取事件并且处理事件,下面是该函数的部分源码
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
int events;
uint32_t revents;
ngx_int_t instance, i;
ngx_event_t *rev, *wev, **queue;
ngx_connection_t *c; //一开始就是等待事件,最长等待时间为timer
events = epoll_wait(ep, event_list, (int) nevents, timer); ...
//循环开始处理收到的所有事件
for (i = ; i < events; i++) {
//ptr成员指向的是ngx_connection_t,但最后一位有特殊含义,需要将它屏蔽掉
c = event_list[i].data.ptr;
instance = (uintptr_t) c & ;
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~); //取出读事件
rev = c->read;
...
//取得发生一个事件
revents = event_list[i].events; ...
//该事件是一个读事件而且是活跃的
if ((revents & EPOLLIN) && rev->active) { ...
//如果设置了NGX_POST_EVENTS标识,事件放入到相应的队列中
if (flags & NGX_POST_EVENTS) {
queue = (ngx_event_t **) (rev->accept ?
&ngx_posted_accept_events : &ngx_posted_events); ngx_locked_post_event(rev, queue);
//否则立刻处理
} else {
rev->handler(rev);
}
} //获取写事件
wev = c->write; if ((revents & EPOLLOUT) && wev->active) { ...
if (flags & NGX_POST_EVENTS) {
ngx_locked_post_event(wev, &ngx_posted_events); } else {
wev->handler(wev);
}
}
}
...
return NGX_OK;
}
ngx_process_events_and_timers流程
在Nginx中,每个worker进程都在ngx_worker_process_cycle方法中循环处理事件。
处理分发事件实际上就是调用的ngx_process_events_and_timers方法,循环调用该函数就是在处理所有的事件,该方法的核心的操作主要有以下3个:
1.调用所使用的事件驱动模块实现的process_events方法,处理网络事件(调用ngx_epoll_process_events方法)。
2.处理两个post事件队列中的事件(调用ngx_event_process_posted方法)。
3.处理定时器时间(调用ngx_event_expire_timers方法)
下图展示了该方法的流程,结合前面的进行理解
事实上,在调用上面循环的方法之前ngx_event_core_module模块会做一些初始化工作:
1.预分配cycle->connection数组充当连接池
2.预分配所有写事件到cycle->read_events数组
3.预分配所有读事件到cycle->write_events数组
(Nginx中连接池跟读写事件都是这个阶段预先分配好的,其大小跟配置项有关)
4.为所有ngx_listening_t监听对象中的connectin成员分配连接,同时对监听端口的读事件设置处理方法为ngx_event_accept(最后有这个方法的介绍)
5.将监听对象连接的读事件添加到事件驱动模块中,这样,epoll等事件模块就开始检测监听服务,并开始向用户提供服务了。
建立新连接
处理新连接事件的回调函数是ngx_event_accept,其原型如下
void ngx_event_accept(ngx_event_t *ev);
下图展示了该方法的流程
Nginx:事件模块的更多相关文章
- nginx事件模块分析(一)
nginx ngx_events_module模块分析 ngx_events_module模块是核心模块之一,它是其它所有事件模块的代理模块.nginx在启动时只与events模块打交道,而由even ...
- 【Nginx】Nginx事件模块
一.事件处理框架概述 事件处理框架所要解决的问题是如何收集.管理.分发事件.事件以网络事件和定时器事件为主,而网络事件中以TCP网络事件为主.事件处理框架需要在不同的操作系统内核中选择一种事件驱动机制 ...
- Nginx功能模块汇总
主要文档 Nginx功能概述.为什么选择Nginx.Nginx安装.常见问题(FAQ).配置符号参考.调试 nginx.优化 Nginx.运行和控制Nginx 核心模块 Nginx事件模块.Nginx ...
- 高性能Web服务器Nginx的配置与部署研究(8)核心模块之事件模块
一.事件模块的作用是什么? 用来设置Nginx处理链接请求. 二.相关指令 1. accept_mutex 含义:设置是否使用连接互斥锁进行顺序的accept()系统调用. 语法:accept_mut ...
- 【Nginx】ngx_event_core_module事件模块
功能:创建连接池,决定使用哪些事件驱动机制,以及初始化将要使用的事件模块 该模块定义了ngx_event_core_commands数组处理其感兴趣的7个配置项 ngx_event_conf_t为该模 ...
- nginx源代码分析--事件模块 & 琐碎
通过HUP信息使得NGINX实现又一次读取配置文件,使用USR2信号使得NGINX实现平滑升级. 在nginx中有模块这么一说,对外全部的模块都是ngx_module_t类型,这个结构体作为全部模块的 ...
- nginx日志模块、事件模块
日志模块 1.access_log指令 语法: access_log path [format [buffer=size [flush=time]]]; access_log logs/access. ...
- Nginx事件管理之ngx_event_core_module模块
1. 概述 ngx_event_core_module 模块是一个事件类型的模块,它在所有事件模块中的顺序是第一位.它主要完成以下两点任务: 创建连接池(包括读/写事件): 决定究竟使用哪些事件驱动机 ...
- Nginx事件管理之核心模块ngx_events_module
1. ngx_events_module核心模块的功能介绍 ngx_events_module 模式是一个核心模块,它的功能如下: 定义新的事件类型 定义每个事件模块都需要实现的ngx_event_m ...
随机推荐
- 渗透协作工具 dradis centos安装
https://dradisframework.com/ce/documentation/install_centos.html yum install rubygems 安装的bundle在drad ...
- fork+exec 与system,popen区别
1.fork + exec fork用来创建一个子进程.一个程序一调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于 ...
- Shiro去掉URL中的JSESSIONID的解决方案
shiro版本在1.3.2版本以上这个BUG已经解决,只需要在配置文件如下配置中添加红色部分即可 <!-- 会话管理器 --> <bean id="sessionManag ...
- Fiddler抓包11-HTTPS证书Actions无法导出问题【转载】
本篇转自博客:上海-悠悠 原文地址:http://www.cnblogs.com/yoyoketang/tag/fiddler/ 前言 在点Actions时候出现Export Failed:The r ...
- ros move_base costmap 理解和实现动态窗口法避障
以下大部分内容参考自 ros_by_example_hydro_volume_1.pdf local costmap 是怎么生成的?跟三维点云有什么关系? global costmap在没有全局地图下 ...
- 用了GradientDrawable后,当点击控件时,控件大小发生变化
android新手:发现一个很奇怪的问题,用了GradientDrawable后,当点击控件时,程序自动使我的一些控件大小保持一致,为什么呢,我就是不想它们保持一致啊 改了好久好久:GradientD ...
- Tomcat配置连接池
Tomcat配置DBCP连接池 配置tomcat服务器的时候,使用到jndi;通过Context配置文件实现配置池对象,通过new initialConext()对象的lookup()获取到数据池对象 ...
- Python的功能模块[0] -> wmi -> 获取 Windows 内部信息
wmi模块 / wmi Module WMI (Windows Management Instrumentation) 模块可用于获取 Windows 内部信息.该模块需要 win32com 的支持, ...
- 线段树【SP1043】GSS1 - Can you answer these queries I
Description 给出了序列\(A_1,A_2,-,A_n\). \(a_i \leq 15007,1 \leq n \leq 50000\).查询定义如下: 查询\((x,y)=max{a_i ...
- 如何隐藏 video 元素的下载按钮
1. 使用 video 元素的 ControlList API <video controls controlsList="nodownload"></video ...