参考资料<深入理解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:事件模块的更多相关文章

  1. nginx事件模块分析(一)

    nginx ngx_events_module模块分析 ngx_events_module模块是核心模块之一,它是其它所有事件模块的代理模块.nginx在启动时只与events模块打交道,而由even ...

  2. 【Nginx】Nginx事件模块

    一.事件处理框架概述 事件处理框架所要解决的问题是如何收集.管理.分发事件.事件以网络事件和定时器事件为主,而网络事件中以TCP网络事件为主.事件处理框架需要在不同的操作系统内核中选择一种事件驱动机制 ...

  3. Nginx功能模块汇总

    主要文档 Nginx功能概述.为什么选择Nginx.Nginx安装.常见问题(FAQ).配置符号参考.调试 nginx.优化 Nginx.运行和控制Nginx 核心模块 Nginx事件模块.Nginx ...

  4. 高性能Web服务器Nginx的配置与部署研究(8)核心模块之事件模块

    一.事件模块的作用是什么? 用来设置Nginx处理链接请求. 二.相关指令 1. accept_mutex 含义:设置是否使用连接互斥锁进行顺序的accept()系统调用. 语法:accept_mut ...

  5. 【Nginx】ngx_event_core_module事件模块

    功能:创建连接池,决定使用哪些事件驱动机制,以及初始化将要使用的事件模块 该模块定义了ngx_event_core_commands数组处理其感兴趣的7个配置项 ngx_event_conf_t为该模 ...

  6. nginx源代码分析--事件模块 &amp; 琐碎

    通过HUP信息使得NGINX实现又一次读取配置文件,使用USR2信号使得NGINX实现平滑升级. 在nginx中有模块这么一说,对外全部的模块都是ngx_module_t类型,这个结构体作为全部模块的 ...

  7. nginx日志模块、事件模块

    日志模块 1.access_log指令 语法: access_log path [format [buffer=size [flush=time]]]; access_log logs/access. ...

  8. Nginx事件管理之ngx_event_core_module模块

    1. 概述 ngx_event_core_module 模块是一个事件类型的模块,它在所有事件模块中的顺序是第一位.它主要完成以下两点任务: 创建连接池(包括读/写事件): 决定究竟使用哪些事件驱动机 ...

  9. Nginx事件管理之核心模块ngx_events_module

    1. ngx_events_module核心模块的功能介绍 ngx_events_module 模式是一个核心模块,它的功能如下: 定义新的事件类型 定义每个事件模块都需要实现的ngx_event_m ...

随机推荐

  1. 转载: CentOS/Linux 解决 SSH 连接慢

    CentOS/Linux 解决 SSH 连接慢 现在连接Linux服务器一般都是使用SSH远程连接的方式.最近新装了一台服务器,发现telnet时速度很快,ping时一切也正常,但SSH连接的时候却很 ...

  2. shortcut(NOIP模拟赛)(裸的排序)

    原题: Description Mirek有一条每天从他家去大学工作的最喜欢的路.这个路径由若干个部分组成,且每个部分是10米长的直线.每一个部分是直线连接(没有拐弯)上一个部分或垂直连接上一个部分. ...

  3. php5.3+ 安装(mysqlnd )

    摘自:http://blog.csdn.net/dragon8299/article/details/6273295 如何安装mysqlnd LINUX环境中,默认情况下,php中的mysql扩展还是 ...

  4. Daemon Process

    Daemon Process 守护进程(Daemon)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待    处理某些发生的事件.守护进程是一种很有用的进程.     Lin ...

  5. vue学习之环境配置

    最近在学习vue,就顺手记录一下... 1. 安装 nodejs https://nodejs.org   -->注:安装LTS的(LTS为长期稳定版本) 在cmd中输入 node -v 如果显 ...

  6. [BZOJ1044][HAOI2008]木棍分割 二分+贪心+dp+前缀和优化

    1044: [HAOI2008]木棍分割 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 4112  Solved: 1577 [Submit][St ...

  7. postgres表空间、模式、表和用户/角色之间的关系

    一. 角色(role)和用户(user)   1. role      postgres=# create role kanon password 'kanon';         #使用role创建 ...

  8. [CF232E]Quick Tortoise

    题目大意: 给你一个$n\times m(n,m\leq 500)$的格子,有一些是障碍物.从一个格子出发只能向下或向右走,有$q$组询问,每次询问从一个点是否能够到达另一个点. 思路: 分治. 两点 ...

  9. 事务没有提交导致 锁等待Lock wait timeout exceeded异常

    异常:Lock wait timeout exceeded; try restarting transaction 解决办法: 执行select * from information_schema.i ...

  10. 玩转Nuget服务器搭建(一)

    背景                                                                                      公司项目是分模块进行架构 ...