文档章节

nginx源码分析——事件模块

hncscwc
 hncscwc
发布于 2016/06/23 11:44
字数 1869
阅读 1K
收藏 2

1. 事件模块概述

事件处理框架所要解决的问题是如何收集,管理,分发事件。这里所说的事件,主要以网络事件和定时器事件为主,而网络事件中又以TCP网络事件为主。由于网络事件与网卡中断处理程序,内核提供的系统调用密切相关,所以网络事件的驱动取决于不同的操作系统平台,在同一操作系统中也受制于不同的操作系统内核版本。因此不同操作系统有不同的事件驱动机制。

基于模块化的设计思想,nginx对于事件处理分不同的模块来完成。首先是ngx_events_module,它是NGX_CORE_MODULE类型的模块,主要负责配置文件events块配置项的解析;其次是ngx_event_core_module,它是NGX_EVENTS_MODULE类型的模块,这个模块会决定使用哪种事件驱动机制,并且怎样调用事件驱动完成事件的管理;最后是ngx_epoll_module,ngx_kqueue_module,ngx_poll_module等一系列模块,这些模块实现了具体的事件驱动机制。

2. 事件模块间的抽象化及初始化流程

在模块接口ngx_module_t中,有一个指向模块上下文的指针,不同的模块采用不同的结构体。

对于NGX_EVENT_MODULE类型的模块,其上下文结构体为ngx_event_module_t:

typedef struct {
    // 事件模块的名称
    ngx_str_t * name;
    // 用于创建保存配置项参数的结构体
    void *      (*create_conf)(ngx_cycle_t * cycle);
    // 结合配置文件初始化配置项参数
    char *      (*init_conf)(ngx_cycle_t * cycle, void * conf);
    // 事件驱动机制的核心方法抽象
    ngx_event_actions_t  actions;
} ngx_event_module_t;

typedef struct {
    // 添加事件方法 负责把一个感兴趣的事件添加到事件驱动中
    ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 删除事件方法
    ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 启用一个事件
    ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 禁用一个事件
    ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 向事件驱动中添加一个新的连接,该连接的读写事件均会被添加到事件驱动中
    ngx_int_t (*add_conn)(ngx_connection_t * c);
    // 从事件驱动中删除一个新的连接
    ngx_int_t (*del_conn)(ngx_connection_t * c, ngx_uint_t flags);
    ngx_int_t (*notify)(ngx_event_handler_pt handler);
    // 处理事件方法
    ngx_int_t (*process_event)(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags);
    // 事件驱动初始化方法
    ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    // 退出事件驱动前调用的方法
    void      (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;

ngx_epoll_module,ngx_kqueue_module,ngx_poll_module等模块均会实现ngx_event_actions_t中定义的方法以此实现对应的事件驱动机制。另外,在事件模块初始化的过程中会对全局变量ngx_event_actions赋值,后续通过ngx_event_actions完成事件的管理,这样逻辑上就有了较明显的分层。

extern ngx_event_actions_t ngx_event_actions;

#define  ngx_process_events  ngx_event_actions.process_events
#define  ngx_done_events     ngx_event_actions.done
#define  ngx_add_event       ngx_event_actions.add
#define  ngx_del_event       ngx_event_actions.del
#define  ngx_add_conn        ngx_event_actions.add_conn
#define  ngx_del_conn        ngx_event_actions.del_conn
#define  ngx_notify          ngx_event_actions.notify

事件模块的运行流程:

  • 事件模块的初始化

    即调用ngx_event_core_module模块的ngx_event_module_init方法,在该方法中会初始化一些与系统相关的信息,例如进程打开的最大文件数,原子锁(文件锁)的初始化等。

  • 模块进程启动初始化

    即调用ngx_event_core_module模块的ngx_event_process_init方法,在该方法中会初始化用于多进程侦听的锁,初始化选用的事件驱动机制、初始化连接池、读写事件、最后将侦听套接字作为可读事件添加到事件驱动中。

  • 事件循环

    通过ngx_event_actions开始事件循环。

3. 事件模块重要的结构体

  • 事件

    每一个事件都由ngx_event_t结构体来表示。该结构体中最核心的部分就是handler回调方法,它由每一个事件消费模块实现,以此决定这个事件究竟如何被处理。

typedef struct ngx_event_s ngx_event_t;
struct ngx_event_s {
    // 事件相关的对象, 通常指向ngx_connection_t连接对象
    void *  data;
    // 事件的处理方法, 每个事件消费模块都会重新实现它
    ngx_event_handler_pt  handler;
    ...
};
  • 连接

    在nginx中,定义了基本的数据结构ngx_connection_t来表示连接。这个连接可以是被动连接:即客户端主动发起的,nginx服务器被动接受的tcp连接;也可以是主动连接:即nginx主动向上游服务器建立的连接,并以此连接与上游服务器通信。主动连接由结构体ngx_peer_connection来表示,它是以ngx_connection_t结构体为基础来实现的。

typedef struct ngx_connection_s ngx_connection_t;
struct ngx_connection_s {
    // 作为空闲连接时, 指向连接池中下一个空闲连接
    // 作为非空闲连接时, 其意义由使用连接的模块定义, 例如http模块将data指向一个http请求(ngx_http_request_t)
    void * data;
    // 连接对应的读事件
    ngx_event_t * read;
    // 连接对应的写事件
    ngx_event_t * write;
    // 连接对应的侦听对象
    ngx_listening_t * listening;
    // 连接的套接字句柄
    ngx_socket_t fd;
    ...
};
  • 侦听

    每个nginx需要侦听的端口都由结构体ngx_listening_t来表示,该结构体中包含了socket套接字,侦听的ip地址,端口,以及侦听端口上成功建立新连接后的回调处理方法。

typedef struct ngx_listening_s ngx_listening_t;
struct ngx_listening_s {
    // 侦听套接字
    ngx_socket_t  fd;
    // 侦听地址
    struct sockaddr * sockaddr;
    // 新连接成功建立后的处理方法
    ngx_connection_handler_pt  handler;
    // 对应的连接对象
    ngx_connection_t * connection;
    ...
};
  • 结构体之间的联系

    在初始化过程中,每个连接都会自动对应到一个读事件和写事件;将侦听套接字添加到事件驱动过程中,为每个侦听套接字分配一个连接,并对分配到的连接的读事件的处理函数赋值;当新连接建立后回调侦听的处理方法,在该方法中会修改新连接读写事件的处理方法,由于侦听是在不同的模块中被初始化的,有了新连接的回调处理方法,不同的模块就能方便的集成事件处理框架。例如http模块在解析配置构造ngx_listening_t结构体时,将其回调处理方法设置为ngx_http_init_connection,当有新连接成功建立时,该函数被回调,并且根据配置与实际情况将新连接的读写事件处理方法修改为ngx_http_wait_request_handler,ngx_http_empty_handler或其他http的处理方法。

相关源码:

static ngx_int_t  ngx_event_process_init( ngx_cycle_t * cycle )
{
    ...
    // 为连接分配内存空间
    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 = 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);
    if( cycle->write_events == NULL ) {
        return NGX_ERROR;
    }
    wev = cycle->write_events;
    for( i = 0; i < cycle->connection_n; i++ ) {
        wev[i].closed = 1;
        wev[i].instance = 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;
    } while(i);

    // 为每个侦听分配一个连接, 用于事件管理
    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->type = ls[i].type;
        c->log = &ls[i].log;
        c->listening = &ls[i];
        ls[i].connection = c;
        rev = c->read;
        rev->log = c->log;
        rev->accept = 1;
        rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept : ngx_event_recvmsg;

        if(ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }
    return NGX_OK;
}

参考:

《深入理解nginx》

《nginx开发从入门到精通》

© 著作权归作者所有

hncscwc
粉丝 70
博文 70
码字总数 76137
作品 0
杭州
程序员
私信 提问
加载中

评论(0)

nginx源码分析—启动流程

作者:阿波 本文链接: http://blog.csdn.net/livelylittlefish/article/details/7243718 Content 0. 序 1. main()分析 2. 注意问题 2.1 几个初值 2.2 nginx工作模式 2.3 一些配置 2.4 其他开...

晨曦之光
2012/03/09
1.6K
0
nginx源码分析—hash结构ngx_hash_t(v1.0.4)

本博客(http://blog.csdn.net/livelylittlefish )贴出作者(阿波)相关研究、学习内容所做的笔记,欢迎广大朋友指正! Content 0.序 1.hash结构 1.1ngx_hash_t结构 1.2ngx_hash_init_t结构...

晨曦之光
2012/03/09
794
0
Nginx 源码分析-事件循环

事件循环这个概念貌似在windows编程中提得更多,Linux程序却很少提及这个概念。本文所提及的事件循环其实就是worker cycle,由于此处将关注的不再是worker进程,而是worker进程在循环过程中关...

红薯
2010/12/20
1.3K
2
nginx源码分析——定时器

概述 nginx实现了自己的定时器触发机制,它与epoll等事件驱动模块处理的网络事件不同;在网络事件中,网络事件的触发是由内核完成的,而定时器事件则完全是由nginx自身实现的,它与内核完全无...

hncscwc
2016/10/31
394
0
nginx源码分析——http处理流程

http处理流程概述 nginx对于http请求处理的大概流程是:接收客户端发起的连接,然后接收并解析http请求行,接收并解析http头部;再根据配置文件nginx.conf找到相关http模块,使这些模块依次合...

hncscwc
2016/07/15
644
2

没有更多内容

加载失败,请刷新页面

加载更多

数组foreach()遍历中的传递引用

在PHP的foreach ($arr as $k => $v) 的遍历中,每一轮的遍历,都发生了以下事情 $k = currentKey($arrCopy); //将副本数组本轮次的键的值分配给$k,比如下标0,1,2等;$v = currentVal($arrCo...

vinci321
今天
128
0
将文件从Docker容器复制到主机 - Copying files from Docker container to host

问题: I'm thinking of using Docker to build my dependencies on a Continuous Integration (CI) server, so that I don't have to install all the runtimes and libraries on the agent......

技术盛宴
今天
98
0
略谈分布式系统中的容器设计模式

本文作者:zytan_cocoa 略谈分布式系统中的容器设计模式 谭中意 2020/3/5 前言:云原生(Cloud Native)不仅仅是趋势,更是现在进行时,它是构建现代的,可弹性伸缩的,快速迭代的计算网络服...

百度开发者中心
03/11
127
0
elasticsearch 第三讲

es的详细介绍 SearchTemplate tmdb 表示的是模板名称 dmdb1 表示的是当前的索引 脚本方式编辑 ##编辑模板POST _scripts/tmdb{ "script": { "lang": "mustache", "source": { ......

鸡蛋炒馒头
今天
207
0
IDEA新建springMVC项目,启动后访问Controller报404

IDEA新建springMVC项目,启动后可以访问到默认的index.jsp,但是访问controller就报404 查看web.xml配置 spingmvc.xml配置 都没有问题 请求的url也和定义的@RequestMapping一致。也没有问题。...

vicky_dimi
今天
303
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部