19

nginx事件模块结构体详解

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzU2NDc4MjE2Ng%3D%3D&%3Bmid=2247484467&%3Bidx=2&%3Bsn=b8f7cf224aef681e08e075781dd95379
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

r26N32u.jpg!web

fym6FvI.gif

前言

事件模块是nginx的核心模块之一,nginx中客户端请求的处理和命令行指令的执行都是基于事件模块进行驱动的。因此,掌握事件模块的实现原理对于我们理解nginx整体架构有非常重要的意义。本文首先会讲解事件模块相关的几个模块定义及其执行流程进行讲解,其源码的讲解将会在后面的文章中进行。

nginx的事件核心模块主要有两个: ngx_events_module ngx_event_core_module 。这两个模块的主要区别在于, ngx_events_module 的类型为 NGX_CORE_MODULE ,其本质上虽然是核心模块类型,但是却是事件模块的一个驱动点,其解析的配置项为 events {} ,并且会创建用于存储事件模块相关配置的结构体;而 ngx_event_core_module 的类型则是 NGX_EVENT_MODULE ,这个模块的配置对象中会存储事件模块的基础配置对象,其主要作用是解析 events{} 配置块中的子配置项。下面我们就来看一下这两个模块的基础配置。

fym6FvI.gif

1. ngx_events_module

如下是 ngx_events_module 模块的基础配置:

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
};

static ngx_core_module_t ngx_events_module_ctx = {
    ngx_string("events"),
    NULL,
    ngx_event_init_conf
};

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
};

ngx_events_module 模块的定义中,其module context指向的是 ngx_events_module_ctx ,也即第二个结构体的配置,而module directives指向的则是ngx_events_commands,也即第三个结构体的定义。可以看到, ngx_events_commands 中只定义了一个 events 配置项,而这个配置项的类型为 NGX_CONF_BLOCK ,也就是说其是一个配置块的类型,这里我们就理解了,这个command对应的就是我们在nginx.conf中使用的 events {} 配置块,而这个配置块的解析则是通过 ngx_events_block() 方法进行的。

我们知道,在nginx的核心配置对象ngx_cycle_t中的conf_ctx数组中,每个模块在数组对应的位置都会有一个配置对象,同理,这里的核心模块也会有一个配置对象,但是上面的第二个结构体 ngx_events_module_ctx 中的第二个属性值为NULL,也就是说这里的事件模块的定义中是没有创建配置对象的方法的,但是却有初始化配置对象的方法,也即第三个属性值 ngx_event_init_conf() 方法。那么这里事件模块的配置对象是在哪里创建的呢?其实其就是在解析配置对象的时候进行的,也即在执行解析 events {} 配置块的 ngx_events_block() 方法中进行的。该方法本质上只是创建了一个指针数组,然后将其赋值给nginx核心和值对象的ngx_cycle_t的conf_ctx的对应位置。

fym6FvI.gif

2. ngx_event_core_module

在介绍 ngx_event_core_module 模块之前,我们首先需要讲解一下事件模块的接口定义:

typedef struct {
    // 事件模块的名称
    ngx_str_t *name;

    // 在解析配置项前,这个回调方法用于创建存储配置项参数的结构体
    void *(*create_conf)(ngx_cycle_t *cycle);

    // 在解析配置项完成后,init_conf()方法会被调用,用以综合处理当前事件模块感兴趣的全部配置项
    char *(*init_conf)(ngx_cycle_t *cycle, void *conf);

    // 对于事件驱动机制,每个事件模块需要实现的10个抽象方法
    ngx_event_actions_t actions;
} ngx_event_module_t;

typedef struct {
    // 添加事件方法,它负责把一个感兴趣的事件添加到操作系统提供的事件驱动机制(epoll、kqueue等)中,
    // 这样,在事件发生后,将可以在调用下面的process_events时获取这个事件
    ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    // 删除事件方法,它把一个已经存在于事件驱动机制中的事件移除,这样以后即使这个事件发生,
    // 调用process_events()方法时也无法再获取这个事件
    ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    // 启用一个事件,目前事件框架不会调用这个方法,大部分事件驱动模块对于该方法的实现都是
    // 与上面的add()方法完全一致的
    ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    // 禁用一个事件,目前事件框架不会调用这个方法,大部分事件驱动模块对于该方法的实现都是
    // 与上面的del()方法完全一致的
    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);

    // 在正常的工作循环中,将通过调用process_events()方法来处理事件。
    // 这个方法仅在ngx_process_events_and_timers()方法中调用,它是处理、分发事件的核心
    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;

nginx的事件模块主要有两个配置结构体: ngx_event_module_t ngx_event_actions_t 两个。从上面的定义中可以看出, ngx_event_module_t 结构体是引用了 ngx_event_actions_t 的。 ngx_event_module_t 的作用主要是创建和初始化当前模块所需要的配置结构体,而 ngx_event_actions_t 则主要定义了当前事件模块处理各个事件的方式,这个接口典型的实现接口是nginx定义的各个事件模型,比如epoll的 ngx_epoll_module_ctx.actions 和kqueue的 ngx_kqueue_module_ctx.actions 。从这里就可以看出,事件模块的定义抽象了各个处理事件的模型的相关处理方法。

对于事件模块而言,其有一个模块是用于存储事件相关的基础配置的,即 ngx_event_core_module ,虽然实现了 ngx_event_module_t 接口,但是其不进行具体的事件处理。如下是 ngx_event_core_module 模块的定义:

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 */
    // 该方法主要是在master进程启动的过程中调用的,用于初始化时间模块
    ngx_event_module_init,                 /* init module */
    // 该方法是在各个worker进程启动之后调用的
    ngx_event_process_init,                /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

static ngx_event_module_t ngx_event_core_module_ctx = {
    &event_core_name,
    ngx_event_core_create_conf,            /* create configuration */
    ngx_event_core_init_conf,              /* init configuration */

    // ngx_event_core_module_ctx并不直接负责TCP网络事件的驱动,
    // 因而这里的ngx_event_actions_t中的方法都为NULL
    {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};

static ngx_command_t ngx_event_core_commands[] = {

    // 连接池的大小,也即每个worker进程中支持的TCP最大连接数,它与connections配置项的意义是重复的
    {ngx_string("worker_connections"),
     NGX_EVENT_CONF | NGX_CONF_TAKE1,
     ngx_event_connections,
     0,
     0,
     NULL},

     // 确定选择哪一个事件模块作为事件驱动机制
    {ngx_string("use"),
     NGX_EVENT_CONF | NGX_CONF_TAKE1,
     ngx_event_use,
     0,
     0,
     NULL},

     // 对应于ngx_event_s中的available属性,对于epoll事件驱动模式来说,意味着在接收到一个新连接事件时,
     // 调用accept以尽可能多地接收连接
    {ngx_string("multi_accept"),
     NGX_EVENT_CONF | NGX_CONF_FLAG,
     ngx_conf_set_flag_slot,
     0,
     offsetof(ngx_event_conf_t, multi_accept),
     NULL},

     // 确定是否使用accept_mutex负载均衡锁,默认为开启
    {ngx_string("accept_mutex"),
     NGX_EVENT_CONF | NGX_CONF_FLAG,
     ngx_conf_set_flag_slot,
     0,
     offsetof(ngx_event_conf_t, accept_mutex),
     NULL},

     // 启用accept_mutex负载均衡锁后,延迟accept_mutex_delay毫秒后再试图处理新连接事件
    {ngx_string("accept_mutex_delay"),
     NGX_EVENT_CONF | NGX_CONF_TAKE1,
     ngx_conf_set_msec_slot,
     0,
     offsetof(ngx_event_conf_t, accept_mutex_delay),
     NULL},

     // 需要对来自指定IP的TCP连接打印debug级别的调试日志
    {ngx_string("debug_connection"),
     NGX_EVENT_CONF | NGX_CONF_TAKE1,
     ngx_event_debug_connection,
     0,
     0,
     NULL},

    ngx_null_command
};

在事件模块的定义中,module context指向的是一个 ngx_event_module_t 结构体,这里的 ngx_event_core_module 的module context指向的就是第二个结构体定义的 ngx_event_core_module_ctx ,而 ngx_event_core_module_ctx 中则定义了当前核心模块创建配置对象和初始化配置对象的方法,可以看到,其actions属性中的值全部为NULL,这是因为该模块并不负责处理具体的事件处理方案,而是负责核心结构体的创建和初始化,nginx也会保证这个模块在所有的事件模块中最先被调用,其余各个事件模块也可以引用该模块所存储的基础配置数据。

ngx_event_core_module 中第三个属性 ngx_event_core_commands 指向的是上面的第三个结构体,这个结构体中定义了当前事件模块所能使用的各个配置项的基本配置以及解析这些配置项的方法。

这里我们需要着重强调 ngx_event_core_module 中的第六个和第七个属性,这两个属性指向的是都是某个方法,第六个属性init module的主要是在nginx启动过程中解析完nginx.conf配置文件之后执行,其作用是对当前模块进行初始化的工作,而第七个属性init process主要是在nginx启动worker进程之后worker进程开始执行主循环之前调用的,其作用是进行worker进程执行前的初始化工作。

fym6FvI.gif

3. 模块方法的执行流程

通过上面的介绍我们大致了解了定义事件模块的两个核心模块的主要方法及其作用,这里则主要是对这些方法的执行流程进行讲解,如下是其流程示意图:

UFBre2Q.png!web

对于上面的,这里需要对其各个步骤的功能进行说明:

  1. 解析nginx.conf文件,当遇到events配置项时,就使用ngx_evetns_block()方法对其进行解析;

  2. 创建用于存储各个事件模块存储配置项的结构体的数组;

  3. 采用递归的方式解析events配置块中的子配置项;

  4. 依次检查事件核心模块的配置项,如果其没有赋值,则对其赋一个默认值;

  5. 检查是否创建了存储事件模块配置项的数组,该检查的主要目的是判断核心模块是否成功初始化了;

  6. 主要是通过解析得到的配置项,设置诸如时间定时器的执行频率、可打开的文件句柄限制和初始化记录统计数据的属性;

  7. 在worker进程中调用,用于初始化worker进程运行所需要的环境,比如初始化事件队列、初始化事件模型、添加时间更新的定时任务等;

fym6FvI.gif

4. 小结

本文首先对nginx事件模块的核心结构体的基本配置和作用进行了详细讲解,然后讲解了这些配置方法的调用流程,并且讲解了各个流程方法的主要作用,下一篇文章将会详细讲解这些流程的具体实现原理。

2Q3yMr7.png!web

在看点这里

iuMJ32N.gif


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK