Web服务器在面对高并发的情况下,网络的IO一般选择IO复用,像apache选择的Select/poll。Nginx在linux 2.6后选择Epoll做网路IO,提高了WEB服务的并发能力。

在本章,我们将看看NGINX如何使用epoll。

首先,我们看一下数据结构图:

1、从结构图中,我们先看第一部分,NGINX拿到socket标示符,绑定本地地址,监听socket标示符信息,由于NGINX支持多server,支持各个server使用不同的端口、不同的协议族。如此需要多个socket句柄来支持,NGINX利用ngx_listening_t这个数据结构来保存打开的socket信息并且放置在一个数组里面。这个过程是在初始化cycle中完成的。

2、关于socket句柄,我们区分两类,一类是上一部分说的服务端申请的socket,这一部分socket处理和客户端连接的情况,如果有新的连接进来,将会产生关于这一批socket的事件;第二类,是连接建立后,服务端accept客户端的socket句柄,针对这个句柄,服务端做读和写的操作。

我们再看一下下图绘制的一个处理流程。

我们再通过代码来看看整个过程

分析配置文件,申请socket fd

2、处理事件模块的初始化

static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
....... //对所有NGX_EVENT_MODULE模块执行module->actions.init
for (m = ; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
} if (ngx_modules[m]->ctx_index != ecf->use) {
continue;
} module = ngx_modules[m]->ctx; if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
/* fatal */
exit();
} break;
} ...... cycle->connections =
ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
if (cycle->connections == NULL) {
return NGX_ERROR;
} c = cycle->connections; cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->read_events == NULL) {
return NGX_ERROR;
} rev = cycle->read_events;
for (i = ; i < cycle->connection_n; i++) {
rev[i].closed = ;
rev[i].instance = ;
#if (NGX_THREADS)
rev[i].lock = &c[i].lock;
rev[i].own_lock = &c[i].lock;
#endif
} cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->write_events == NULL) {
return NGX_ERROR;
} wev = cycle->write_events;
for (i = ; i < cycle->connection_n; i++) {
wev[i].closed = ;
#if (NGX_THREADS)
wev[i].lock = &c[i].lock;
wev[i].own_lock = &c[i].lock;
#endif
} 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) -; next = &c[i]; #if (NGX_THREADS)
c[i].lock = ;
#endif
} while (i); cycle->free_connections = next;
cycle->free_connection_n = cycle->connection_n; /* for each listening socket */ ls = cycle->listening.elts;
for (i = ; i < cycle->listening.nelts; i++) { //获取空余连接,将服务端申请的每个socket fd占用一个连接,
c = ngx_get_connection(ls[i].fd, cycle->log); ......
rev->handler = ngx_event_accept; 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 {
//如果使用epoll模块,则在epoll上注册读事件,操作类型是EPOLL_CTL_ADD,也就是关注服务端申请的socket fd是否有新连接申请
if (ngx_add_event(rev, NGX_READ_EVENT, ) == NGX_ERROR) {
return NGX_ERROR;
}
} } return NGX_OK;
}

3、  EPOLL在发现新事件后,会查找处理该事件的方法,实际上先查找对应的连接,该连接指针是如下方式,利用epoll_event结构体成员data的ptr指针可以用来存储用户的连接信息,我们在看看后面是怎么处理的

static ngx_int_t
ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
.......
//epoll_event结构体成员data的ptr指针可以用来存储用户的指针信息,这里用来存储连接信息,其中事件的instance标识主要用来显示事件是否过期 ee.events = events | (uint32_t) flags;
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); //把instance加到ee的末尾(由于内存对齐,末尾一般为0) ……. 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;
}
....... return NGX_OK;
}

4、

static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
...... ngx_worker_process_init(cycle, worker); ...... for ( ;; ) { ......
ngx_process_events_and_timers(cycle);
......
}
}

5、

void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
......
(void) ngx_process_events(cycle, timer, flags);
...... }

Nginx源码研究三:Epoll在NGINX中的使用的更多相关文章

  1. Chrome自带恐龙小游戏的源码研究(三)

    在上一篇<Chrome自带恐龙小游戏的源码研究(二)>中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替. 昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制. ...

  2. Nginx源码研究七:nginx的location指令分析

    在nginx的配置文件nginx.conf中,我们在配置server的时候,会配置一下location指令,这个location指令是提供给用户来配置对于符合指令的http请求,采用该指令内部的处理方 ...

  3. Nginx源码研究一:NGINX模块启动

    Nginx 是一个轻量级,但是高性能的 HTTP 和 代理 服务器,也是一个 IMAP/POP3/SMTP代理服务器. 它的第一个版本0.1.0是由俄罗斯的工程师Igor Sysoev与2004年10 ...

  4. Nginx源码研究六:NGINX的配置文件分析

    上一篇写到nginx的各个模块的配置信息的存储结构,大体描述了对配置信息的配置项生成,定制,初始化过程.这里重点研究实现定制的过程,所谓实现定制,这里指的是,nginx系统提供使用者定义nginx的配 ...

  5. [nginx] nginx源码分析--proxy模式下nginx的自动重定向auto_redirect

    描述 我们配置了一个proxy模式下的nginx, upstream backend-test { server ; } server { listen ; location = /nginx/hww ...

  6. Nginx源码研究二:NGINX的事件处理概论

    NGINX作为服务端的应用程序,在客户端发出数据后,服务端在做着这样一些处理,数据先会经过网卡,网卡会和操作系统做交互,经过操作系统的协议栈处理,再和不同的应用程序交互. 在这里面涉及两个概念,一个是 ...

  7. Nginx源码研究五:NGINX的配置信息管理

    配置信息是nginx系统重要的组成部分,配置信息的使用,实际上包含两层,一层是用户针对参数定义了值,例如下面nginx参数文件中的 keepalive_timeout 65,还有一部分是用户没有定义值 ...

  8. Nginx源码研究八:nginx监听socket实现流程

    前面描述了nginx系统分析nginx的配置文件,初始化模块相关参数的过程,这里利用nginx监听socket的实现过程,做一次完整的回顾 1.首先,nginx启动的main函数中,会先初始化cycl ...

  9. Nginx源码研究四:NGINX的内存管理

    关于nginx的内存使用,我们先看代码,下面是nginx_cycle.c中对全局数据结构cycle的初始化过程 pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, ...

随机推荐

  1. hibernate之CRUD操作

    CRUD是指在做计算处理时的增加(Create).读取(Retrieve)(重新得到数据).更新(Update)和删除(Delete)几个单词的首字母简写. 下面列举实例来讲解这几个操作: 实体类: ...

  2. Quartz与Spring集成 Job如何自动注入Spring容器托管的对象

    在Spring中使用Quartz有两种方式实现:第一种是任务类继承QuartzJobBean,第二种则是在配置文件里定义任务类和要执行的方法,类和方法可以是普通类.很显然,第二种方式远比第一种方式来的 ...

  3. 使用 CustomScript 扩展程序自动执行 Linux 虚拟机自定义任务

    NingKuangWSSC WS ARD 高级项目经理 您可能已经从Windows扩展程序博客中了解了针对 Windows 虚拟机的 CustomScript扩展程序,现在的好消息是,这一扩展程序也已 ...

  4. Pascal's Triangle II —LeetCode

    Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3,Return [1,3, ...

  5. HDOJ(HDU) 2088 Box of Bricks(平均值)

    Problem Description Little Bob likes playing with his box of bricks. He puts the bricks one upon ano ...

  6. 【6】JAVA---地址App小软件(QueryPanel.class)(表现层)

    查找模块: 年龄可进行段查找. 其他的都是模糊匹配. 空格为无用字符,会屏蔽的(除年龄). (如果在年龄中输入空格,会出现异常,当时没想到这点,要防护这点很容易的,但因为在这个小软件的编写过程,我主要 ...

  7. sql为了实现转换的行列

    全名 学科 成绩 牛芬 语文 81 牛芬 数学 88 牛芬 英语 84 张三 语文 90 张三 数学 98 张三 英语 90 (表一) 现有一个表如(表一) 姓名 语文 数学 英语 牛芬 81 88 ...

  8. Android之Notification的多种用法

    我们在用手机的时候,如果来了短信,而我们没有点击查看的话,是不是在手机的最上边的状态栏里有一个短信的小图标提示啊?你是不是也想实现这种功能呢?今天的Notification就是解决这个问题的. 我们也 ...

  9. js进制转换

    var n = 17; var n2 = n.toString(2); var n8 = "0" + n.toString(8); var n16 = "0x" ...

  10. Java数据库连接之配置ODBC数据源

    java使用JDBC-ODBC桥接连接SQLServer数据库需要配置ODBC数据源,配置步骤如下: 1.进入控制面板,找到管理工具 2.看到ODBC数据源,有64位和32位的,如果你的数据库是64位 ...