前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >nginx事件模块源码分析

nginx事件模块源码分析

原创
作者头像
stan1ey
修改于 2021-08-11 06:48:29
修改于 2021-08-11 06:48:29
68900
代码可运行
举报
文章被收录于专栏:安全开发记录安全开发记录
运行总次数:0
代码可运行

事件模块概述

事件处理框架所要解决的问题是如何收集,管理,分发事件。这里所说的事件,主要以网络事件和定时器事件为主,而网络事件中又以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等一系列模块,这些模块实现了具体的事件驱动机制。

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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完成事件的管理,这样逻辑上就有了较明显的分层。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

事件模块的运行流程

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

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

3、事件循环,通过ngx_event_actions开始事件循环。

事件模块重要的结构体

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef struct ngx_event_s ngx_event_t;
struct ngx_event_s {
    // 事件相关的对象, 通常指向ngx_connection_t连接对象
    void *  data;
    // 事件的处理方法, 每个事件消费模块都会重新实现它
    ngx_event_handler_pt  handler;
    ...
};

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
    ...
};

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
    ...
};

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

相关源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
nginx0.1.0之event模块初始化源码分析(4)
event的配置解析相关的代码已经分析完毕。下面分析一下另一个流程中event模块的实现。即在nginx创建进程,并且开始执行进程里的代码的时候。入口函数是ngx_worker_process_cycle。
theanarkh
2019/03/06
3900
nginx源码阅读(4)单进程epoll流程解析
我们这里以单进程启动为例 nginx.c中的main 函数调用ngx_single_process_cycle
golangLeetcode
2022/08/02
4330
万字多图,搞懂 Nginx 高性能网络工作原理!
在单进程的网络编程模型中。所有的网络相关的动作都是在一个进程里完成的,如监听 socket 的创建, bind、listen。再比如 epoll 的创建、要监听事件的添加,以及 epoll_wait 等待时间发生。这些统统都是在一个进程里搞定。
开发内功修炼
2022/05/09
5550
万字多图,搞懂 Nginx 高性能网络工作原理!
Nginx(二): worker 进程处理流程框架解析
Nginx 启动起来之后,会有几个进程运行:1. master 进程接收用户命令并做出响应; 2. worker 进程负责处理各网络事件,并同时接收来自master的处理协调命令;
烂猪皮
2021/01/28
1.3K0
Nginx(二): worker 进程处理流程框架解析
nginx源码阅读(6)http处理流程
Nginx作为一款开源的、高性能的HTTP服务器和反向代理服务器而闻名,本文基于nginx-1.15.0,将为读者简要介绍其HTTP处理流程。
golangLeetcode
2022/08/02
1.1K0
nginx源码阅读(6)http处理流程
nginx源码阅读(1)
Nginx框架是围绕着ngx_cycle_t结构体运行的。ngx_cycle_t结构体中包含的信息主要可以分为以下部分:
golangLeetcode
2022/08/02
7480
Nginx的模块化设计
高度模块化的设计是 Nginx 的架构基础。Nginx主框架中只提供了少量的核心代码,大量强大的功能是在各模块中实现的。
mazhen
2023/11/24
5000
Nginx的模块化设计
Nginx 的异步非阻塞体现在哪里?从理论分析到源码验证
2、那业务层面的异步是怎么个异步法?同步异步的概念我就不说了,前面文章有。异步最重要的标志就是通知,通知,通知!!!
看、未来
2021/10/13
9270
高并发服务器的设计--架构与瓶颈的设计
做架构设计,难免有时候被人问及系统的瓶颈在哪,那首先来了解下什么是瓶颈? 打个形象的比方,人的嘴巴可以吞下一整个面包,但是却咽不下去,因为食管不给力,它比较细,所以嘴巴能吞下的食物大小要受到食管的粗细
李海彬
2018/03/23
2K0
高并发服务器的设计--架构与瓶颈的设计
Nginx(三):http模块的处理流程解析之正向代理
无疑,在nginx的核心服务中,http服务占据了相当大的份量。那么,要想多了解nginx多一点,则必须要了解其http模块的工作机制。
烂猪皮
2021/01/28
2K0
Nginx(三):http模块的处理流程解析之正向代理
nginx0.1.0之event模块初始化源码分析(3)
前面已经分析了event初始化的整体流程和第一步create_conf,接下来看一下第二步ngx_conf_parse。这里不分析该函数的代码,该函数主要是遍历配置文件的内容,然后读取命令和参数。最后匹配nginx所有模块的配置,找到处理该指令的函数。我们首先看一下event模块中ngx_event_core_module模块的指令配置。
theanarkh
2019/03/06
5670
nginx dns解析源码分析
在使用同步IO的情况下,调用gethostbyname()或者gethostbyname_r()就可以根据域名查询到对应的IP地址,。
stan1ey
2021/06/07
1.8K0
nginx dns解析源码分析
nginx1.17.9源码分析之线程池
我们发现事件驱动的软件都得配一个线程池。libuv和nginx都是。因为事件驱动的软件是单线程。但是有些事情总会引起线程阻塞。所以这个事情就不能放到主线程里做。这就是为什么事件驱动都要配一个线程池。把任务交给线程池中的线程。主线程继续执行。任务完成后通知主线程或者执行回调就行。 我们先看一下nginx线程池的架构。然后开始分析。
theanarkh
2020/03/31
5930
nginx1.17.9源码分析之线程池
nginx的timeout(基于nginx1.17.9)
nginx中使用timeout的地方非常多,本文主要分析客户端和nginx通信时涉及到的几个timeout。
theanarkh
2020/06/19
8240
【Nginx 源码学习】Nginx 架构设计与主流程分析
1、nginx在启动后,在unix系统中会以daemon的方式在后台运行,后台进程包含一个master进程和多个worker进程。 2、nginx采用了异步非阻塞的方式来处理请求。
看、未来
2022/05/06
1.3K0
【Nginx 源码学习】Nginx 架构设计与主流程分析
nginx0.1.0之event模块初始化源码分析(1)
nginx模块初始化的流程在下面的代码中,核心模块的初始化,各核心模块首先在create_conf中创建保存配置的数据结构,然后在ngx_conf_parse中,通过解析命令,执行对应的命令处理函数,完成赋值和各核心模块的子模块初始化。最后,如果在ngx_conf_parse时,没有设置值,则执行init_conf函数进行默认初始化。
theanarkh
2019/03/06
4910
nginx event框架总结
Nginx 的 event(事件)处理机制是nginx的核心功能。nginx抽象了event机制,在多个平台有不同的event调用实现方法。比如说经常用的AIO(异步IO),/dev/poll(Solaris 和 Unix 特有),epoll(Linux 特有),kqueue(BSD 特有),poll,select 等。
mariolu
2019/01/23
3.4K0
nginx流程源码分析
nginx作为一个web服务器肯定是要监听套接字的,监听套接字用于接收HTTP请求,其是根据配置文件的内容来创建的。
stan1ey
2021/08/11
5570
nginx流程源码分析
Nginx——事件驱动机制(雷霆追风问题,负载均衡)
所有的worker进程都在ngx_worker_process_cycle方法中循环处理事件,处理分发事件则在ngx_worker_process_cycle方法中调用ngx_process_events_and_timers方法,循环调用该方法就是 在处理全部事件,这正是事件驱动机制的核心。该方法既会处理普通的网络事件,也会处理定时器事件。
全栈程序员站长
2022/07/06
4020
Nginx——事件驱动机制(雷霆追风问题,负载均衡)
nginx upstream模块完整逻辑源码分析
1.启动upstream。 2.连接上游服务器。 3.向上游发送请求。 4.接收上游响应(包头/包体)。 5.结束请求。
stan1ey
2021/01/23
3K0
相关推荐
nginx0.1.0之event模块初始化源码分析(4)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文