欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

Nginx 事件模块

程序员文章站 2022-05-06 21:17:11
...
概述

Nginx 是以事件的触发来驱动的,事件驱动模型主要包括事件收集、事件发送、事件处理(即事件管理)三部分。在Nginx 的工作进程中主要关注的事件是 IO 网络事件 和 定时器事件。在生成的 objs 目录文件中,其中ngx_modules.c 文件的内容是 Nginx 各种模块的执行顺序,我们可以从该文件的内容中看到事件模块的执行顺序为以下所示:注意:由于是在 Linux 系统下,所以支持具体的 epoll 事件模块,接下来的文章结构按照以下顺序来写。

extern ngx_module_t  ngx_events_module;
extern ngx_module_t  ngx_event_core_module;
extern ngx_module_t  ngx_epoll_module;

事件模块接口

ngx_event_module_t 结构体

Nginx 中,结构体 ngx_module_tNginx 模块最基本的接口。对于每一种不同类型的模块,都有一个具体的结构体来描述这一类模块的通用接口,该接口由ngx_module_t 中的成员 ctx 管理。在 Nginx 中定义了事件模块的通用接口ngx_event_module_t 结构体,该结构体定义在文件src/event/ngx_event.h 中:

/* 事件驱动模型通用接口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);

    /* 每个事件模块具体实现的方法,有10个方法,即IO多路复用模型的统一接口 */
    ngx_event_actions_t     actions;
} ngx_event_module_t;

ngx_event_module_t 结构体中actions 的类型是 ngx_event_actions_t 结构体,该成员结构实现了事件驱动模块的具体方法。该结构体定义在文件 src/event/ngx_event.h 中:

/* IO多路复用模型的统一接口 */
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  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
    /* 等待事件的发生,并对事件进行处理 */
    ngx_int_t  (*process_events)(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_event_t 结构体

Nginx 中,每一个具体事件的定义由结构体ngx_event_t 来表示,该结构体 ngx_event_t 用来保存具体事件。该结构体定义在文件 src/event/ngx_event.h 中:

/* 描述每一个事件的ngx_event_t结构体 */
struct ngx_event_s {
    /* 事件相关对象的数据,通常指向ngx_connect_t连接对象 */
    void            *data;

    /* 标志位,为1表示事件可写,即当前对应的TCP连接状态可写 */
    unsigned         write:1;

    /* 标志位,为1表示事件可以建立新连接 */
    unsigned         accept:1;

    /* used to detect the stale events in kqueue, rtsig, and epoll */
    unsigned         instance:1;

    /*
     * the event was passed or would be passed to a kernel;
     * in aio mode - operation was posted.
     */
    /* 标志位,为1表示事件处于活跃状态 */
    unsigned         active:1;

    /* 标志位,为1表示禁用事件 */
    unsigned         disabled:1;

    /* the ready event; in aio mode 0 means that no operation can be posted */
    /* 标志位,为1表示当前事件已经准备就绪 */
    unsigned         ready:1;

    /* 该标志位只用于kqueue,eventport模块,对Linux上的驱动模块没有任何意义 */
    unsigned         oneshot:1;

    /* aio operation is complete */
    /* 该标志位用于异步AIO事件处理 */
    unsigned         complete:1;

    /* 标志位,为1表示当前处理的字符流已经结束 */
    unsigned         eof:1;
    /* 标志位,为1表示当前事件处理过程中出错 */
    unsigned         error:1;

    /* 标志位,为1表示当前事件已超时 */
    unsigned         timedout:1;
    /* 标志位,为1表示当前事件存在于定时器中 */
    unsigned         timer_set:1;

    /* 标志位,为1表示当前事件需要延迟处理 */
    unsigned         delayed:1;

    /*
     * 标志位,为1表示TCP建立需要延迟,即完成建立TCP连接的三次握手后,
     * 不会立即建立TCP连接,直到接收到数据包才建立TCP连接;
     */
    unsigned         deferred_accept:1;

    /* the pending eof reported by kqueue, epoll or in aio chain operation */
    /* 标志位,为1表示等待字符流结束 */
    unsigned         pending_eof:1;

    /* 标志位,为1表示处理post事件 */
    unsigned         posted:1;

#if (NGX_WIN32)
    /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */
    unsigned         accept_context_updated:1;
#endif

#if (NGX_HAVE_KQUEUE)
    unsigned         kq_vnode:1;

    /* the pending errno reported by kqueue */
    int              kq_errno;
#endif

    /*
     * kqueue only:
     *   accept:     number of sockets that wait to be accepted
     *   read:       bytes to read when event is ready
     *               or lowat when event is set with NGX_LOWAT_EVENT flag
     *   write:      available space in buffer when event is ready
     *               or lowat when event is set with NGX_LOWAT_EVENT flag
     *
     * iocp: TODO
     *
     * otherwise:
     *   accept:     1 if accept many, 0 otherwise
     */

#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)
    int              available;
#else
    /* 标志位,在epoll事件机制中表示一次尽可能多地建立TCP连接 */
    unsigned         available:1;
#endif

    /* 当前事件发生时的处理方法 */
    ngx_event_handler_pt  handler;


#if (NGX_HAVE_AIO)

#if (NGX_HAVE_IOCP)
    ngx_event_ovlp_t ovlp;
#else
    /* Linux系统aio机制中定义的结构体 */
    struct aiocb     aiocb;
#endif

#endif

    /* epoll机制不使用该变量 */
    ngx_uint_t       index;

    /* 日志记录 */
    ngx_log_t       *log;

    /* 定时器 */
    ngx_rbtree_node_t   timer;

    /* the posted queue */
    ngx_queue_t      queue;

    /* 标志位,为1表示当前事件已经关闭 */
    unsigned         closed:1;

    /* to test on worker exit */
    unsigned         channel:1;
    unsigned         resolver:1;

    unsigned         cancelable:1;


#if 0

    /* the threads support */

    /*
     * the event thread context, we store it here
     * if $(CC) does not understand __thread declaration
     * and pthread_getspecific() is too costly
     */

    void            *thr_ctx;

#if (NGX_EVENT_T_PADDING)

    /* event should not cross cache line in SMP */

    uint32_t         padding[NGX_EVENT_T_PADDING];
#endif
#endif
};

在每个事件结构体 ngx_event_t 最重要的成员是handler 回调函数,该回调函数定义了当事件发生时的处理方法。该回调方法原型在文件src/core/ngx_core.h 中:

typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);

ngx_connection_t 结构体

当客户端向 Nginx 服务器发起连接请求时,此时若Nginx 服务器被动接收该连接,则相对 Nginx 服务器来说称为被动连接,被动连接的表示由基本数据结构体 ngx_connection_t 完成。该结构体定义在文件 src/core/ngx_connection.h 中:

/* TCP连接结构体 */
struct ngx_connection_s {
    /*
     * 当Nginx服务器产生新的socket时,
     * 都会创建一个ngx_connection_s 结构体,
     * 该结构体用于保存socket的属性和数据;
     */

    /*
     * 当连接未被使用时,data充当连接池中空闲连接表中的next指针;
     * 当连接被使用时,data的意义由具体Nginx模块决定;
     */
    void               *data;
    /* 设置该链接的读事件 */
    ngx_event_t        *read;
    /* 设置该连接的写事件 */
    ngx_event_t        *write;

    /* 用于设置socket的套接字描述符 */
    ngx_socket_t        fd;

    /* 接收网络字符流的方法,是一个函数指针,指向接收函数 */
    ngx_recv_pt         recv;
    /* 发送网络字符流的方法,是一个函数指针,指向发送函数 */
    ngx_send_pt         send;
    /* 以ngx_chain_t链表方式接收网络字符流的方法 */
    ngx_recv_chain_pt   recv_chain;
    /* 以ngx_chain_t链表方式发送网络字符流的方法 */
    ngx_send_chain_pt   send_chain;

    /*
     * 当前连接对应的ngx_listening_t监听对象,
     * 当前连接由ngx_listening_t成员的listening监听端口的事件建立;
     * 成员connection指向当前连接;
     */
    ngx_listening_t    *listening;

    /* 当前连接已发生的字节数 */
    off_t               sent;

    /* 记录日志 */
    ngx_log_t          *log;

    /* 内存池 */
    ngx_pool_t         *pool;

    /* 对端的socket地址sockaddr属性*/
    struct sockaddr    *sockaddr;
    socklen_t           socklen;
    /* 字符串形式的IP地址 */
    ngx_str_t           addr_text;

    ngx_str_t           proxy_protocol_addr;

#if (NGX_SSL)
    ngx_ssl_connection_t  *ssl;
#endif

    /* 本端的监听端口对应的socket的地址sockaddr属性 */
    struct sockaddr    *local_sockaddr;
    socklen_t           local_socklen;

    /* 用于接收、缓存对端发来的字符流 */
    ngx_buf_t          *buffer;

    /*
     * 表示将当前连接作为双向连接中节点元素,
     * 添加到ngx_cycle_t结构体的成员
     * reuseable_connections_queue的双向链表中;
     */
    ngx_queue_t         queue;

    /* 连接使用次数 */
    ngx_atomic_uint_t   number;

    /* 处理请求的次数 */
    ngx_uint_t          requests;

    unsigned            buffered:8;

    unsigned            log_error:3;     /* ngx_connection_log_error_e */

    /* 标志位,为1表示不期待字符流结束 */
    unsigned            unexpected_eof:1;
    /* 标志位,为1表示当前连接已经超时 */
    unsigned            timedout:1;
    /* 标志位,为1表示处理连接过程出错 */
    unsigned            error:1;
    /* 标志位,为1表示当前TCP连接已经销毁 */
    unsigned            destroyed:1;

    /* 标志位,为1表示当前连接处于空闲状态 */
    unsigned            idle:1;
    /* 标志位,为1表示当前连接可重用 */
    unsigned            reusable:1;
    /* 标志为,为1表示当前连接已经关闭 */
    unsigned            close:1;

    /* 标志位,为1表示正在将文件的数据发往对端 */
    unsigned            sendfile:1;
    /*
     * 标志位,若为1,则表示只有连接对应的发送缓冲区满足最低设置的阈值时,
     * 事件驱动模块才会分发事件;
     */
    unsigned            sndlowat:1;
    unsigned            tcp_nodelay:2;   /* ngx_connection_tcp_nodelay_e */
    unsigned            tcp_nopush:2;    /* ngx_connection_tcp_nopush_e */

    unsigned            need_last_buf:1;

#if (NGX_HAVE_IOCP)
    unsigned            accept_context_updated:1;
#endif

#if (NGX_HAVE_AIO_SENDFILE)
    /* 标志位,为1表示使用异步IO方式将磁盘文件发送给网络连接的对端 */
    unsigned            aio_sendfile:1;
    unsigned            busy_count:2;
    /* 使用异步IO发送文件时,用于待发送的文件信息 */
    ngx_buf_t          *busy_sendfile;
#endif

#if (NGX_THREADS)
    ngx_atomic_t        lock;
#endif
};

在处理请求的过程中,若 Nginx 服务器主动向上游服务器建立连接,完成连接建立并与之进行通信,这种相对Nginx 服务器来说是一种主动连接,主动连接由结构体 ngx_peer_connection_t 表示,但是该结构体 ngx_peer_connection_t 也是 ngx_connection_t 结构体的封装。该结构体定义在文件src/event/ngx_event_connect.h 中:

/* 主动连接的结构体 */
struct ngx_peer_connection_s {
    /* 这里是对ngx_connection_t连接结构体的引用 */
    ngx_connection_t                *connection;

    /* 远端服务器的socket的地址sockaddr信息 */
    struct sockaddr                 *sockaddr;
    socklen_t                        socklen;
    /* 远端服务器的名称 */
    ngx_str_t                       *name;

    /* 连接重试的次数 */
    ngx_uint_t                       tries;

    /* 获取连接的方法 */
    ngx_event_get_peer_pt            get;
    /* 释放连接的方法 */
    ngx_event_free_peer_pt           free;
    /* 配合get、free使用 */
    void                            *data;

#if (NGX_SSL)
    ngx_event_set_peer_session_pt    set_session;
    ngx_event_save_peer_session_pt   save_session;
#endif

#if (NGX_THREADS)
    ngx_atomic_t                    *lock;
#endif

    /* 本地地址信息 */
    ngx_addr_t                      *local;

    /* 接收缓冲区 */
    int                              rcvbuf;

    /* 记录日志 */
    ngx_log_t                       *log;

    /* 标志位,为1表示connection连接已经缓存 */
    unsigned                         cached:1;

                                     /* ngx_connection_log_error_e */
    unsigned                         log_error:2;
};

ngx_events_module 核心模块

ngx_events_module 核心模块的定义

ngx_events_module 模块是事件的核心模块,该模块的功能是:定义新的事件类型,并为每个事件模块定义通用接口ngx_event_module_t 结构体,管理事件模块生成的配置项结构体,并解析事件类配置项。首先,看下该模块在文件src/event/ngx_event.c 中的定义:

/* 定义事件核心模块 */
ngx_module_t  ngx_events_module = {
    NGX_MODULE_V1,
    &ngx_events_module_ctx,                /* module context */
    ngx_events_commands,                   /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

其中,模块的配置项指令结构 ngx_events_commands 决定了该模块的功能。配置项指令结构ngx_events_commands 在文件 src/event/ngx_event.c 中定义如下:

/* 配置项结构体数组 */
static ngx_command_t  ngx_events_commands[] = {

    { ngx_string("events"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_events_block,
      0,
      0,
      NULL },

      ngx_null_command
};

从配置项结构体中可以知道,该模块只对 events{...} 配置块感兴趣,并定义了管理事件模块的方法ngx_events_blockngx_events_block 方法在文件src/event/ngx_event.c 中定义:

/* 管理事件模块 */
static char *
ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char                 *rv;
    void               ***ctx;
    ngx_uint_t            i;
    ngx_conf_t            pcf;
    ngx_event_module_t   *m;

    if (*(void **) conf) {
        return "is duplicate";
    }

    /* count the number of the event modules and set up their indices */

    /* 计算模块类中模块的总数,并初始化模块在模块类中的序号 */
    ngx_event_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }

        ngx_modules[i]->ctx_index = ngx_event_max_module++;
    }

    ctx = ngx_pcalloc(cf->pool, sizeof(void *));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    /* 分配指针数组,用于存储所有事件模块生成的配置项结构体指针 */
    *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
    if (*ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    *(void **) conf = ctx;

    /* 若是事件模块,并且定义了create_conf方法,则调用该方法创建存储配置项参数的结构体 */
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }

        m = ngx_modules[i]->ctx;

        if (m->create_conf) {
            (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);
            if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }

    /* 初始化配置项结构体cf */
    pcf = *cf;
    cf->ctx = ctx;/* 描述事件模块的配置项结构 */
    cf->module_type = NGX_EVENT_MODULE;/* 当前解析指令的模块类型 */
    cf->cmd_type = NGX_EVENT_CONF;/* 当前解析指令的指令类型 */

    /* 为所有事件模块解析配置文件nginx.conf中的event{}块中的指令 */
    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv != NGX_CONF_OK)
        return rv;

    /* 遍历所有事件模块,若定义了init_conf方法,则调用该方法用于处理事件模块感兴趣的配置项 */
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }

        m = ngx_modules[i]->ctx;

        if (m->init_conf) {
            rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);
            if (rv != NGX_CONF_OK) {
                return rv;
            }
        }
    }

    return NGX_CONF_OK;
}

另外,在 ngx_events_module 模块的定义中有一个成员ctx 指向了核心模块的通用接口结构。核心模块的通用接口结构体定义在文件src/core/ngx_conf_file.h 中:

/* 核心模块的通用接口结构体 */
typedef struct {
    /* 模块名称 */
    ngx_str_t             name;
    /* 解析配置项前,调用该方法 */
    void               *(*create_conf)(ngx_cycle_t *cycle);
    /* 完成配置项解析后,调用该函数 */
    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;

因此,ngx_events_module 作为核心模块,必须定义核心模块的通用接口结构。ngx_events_module 模块的核心模块通用接口在文件src/event/ngx_event.c 中定义:

/* 实现核心模块通用接口 */
static ngx_core_module_t  ngx_events_module_ctx = {
    ngx_string("events"),
    NULL,
    /*
     * 以前的版本这里是NULL,现在实现了一个获取events配置项的函数,*
     * 但是没有什么作用,因为每个事件模块都会去获取events配置项,
     * 并进行解析与处理;
     */
    ngx_event_init_conf
};

所有事件模块的配置项管理

Nginx 服务器在结构体 ngx_cycle_t 中定义了一个四级指针成员 conf_ctx,整个Nginx 模块都是使用该四级指针成员管理模块的配置项结构,以下events 模块为例对该四级指针成员进行简单的分析,如下图所示:

Nginx 事件模块

每个事件模块可以通过宏定义 ngx_event_get_conf 获取它在create_conf 中分配的结构体的指针;该宏中定义如下:

#define ngx_event_get_conf(conf_ctx, module) \
(*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index];

/* 其中 ngx_get_conf 定义如下 */
#define ngx_get_conf(conf_ctx, module) conf_ctx[module.index]

从上面的宏定义可以知道,每个事件模块获取自己在 create_conf 中分配的结构体的指针,只需在ngx_event_get_conf 传入参数 ngx_cycle_t 中的 conf_ctx 成员,并且传入自己模块的名称即可获取自己分配的结构体指针。

ngx_event_core_module 事件模块

ngx_event_core_module 模块是一个事件类型的模块,它在所有事件模块中的顺序是第一,是其它事件类模块的基础。它主要完成以下任务:

  1. 创建连接池;
  2. 决定使用哪些事件驱动机制;
  3. 初始化将要使用的事件模块;

ngx_event_conf_t 结构体

ngx_event_conf_t 结构体是用来保存ngx_event_core_module 事件模块配置项参数的。该结构体在文件src/event/ngx_event.h 中定义:

/* 存储ngx_event_core_module事件模块配置项参数的结构体 ngx_event_conf_t */
typedef struct {
    /* 连接池中最大连接数 */
    ngx_uint_t    connections;
    /* 被选用模块在所有事件模块中的序号 */
    ngx_uint_t    use;

    /* 标志位,为1表示可批量建立连接 */
    ngx_flag_t    multi_accept;
    /* 标志位,为1表示打开负载均衡锁 */
    ngx_flag_t    accept_mutex;

    /* 延迟建立连接 */
    ngx_msec_t    accept_mutex_delay;

    /* 被使用事件模块的名称 */
    u_char       *name;

#if (NGX_DEBUG)
    /* 用于保存与输出调试级别日志连接对应客户端的地址信息 */
    ngx_array_t   debug_connection;
#endif
} ngx_event_conf_t;

ngx_event_core_module 事件模块的定义

该模块在文件 src/event/ngx_event.c 中定义:

/* 事件模块的定义 */
ngx_module_t  ngx_event_core_module = {
    NGX_MODULE_V1,
    &ngx_event_core_module_ctx,            /* module context */
    ngx_event_core_commands,               /* module directives */
    NGX_EVENT_MODULE,                      /* module type */
    NULL,                                  /* init master */
    ngx_event_module_init,                 /* init module */
    ngx_event_process_init,                /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

其中,模块的配置项指令结构 ngx_event_core_commands