首頁  >  文章  >  運維  >  關於nginx事件模組結構體的詳解

關於nginx事件模組結構體的詳解

藏色散人
藏色散人轉載
2020-01-21 15:22:473385瀏覽

事件模組是nginx的核心模組之一,nginx中客戶端請求的處理和命令列指令的執行都是基於事件模組進行驅動的。因此,掌握事件模組的實作原理對於我們理解nginx整體架構有非常重要的意義。

關於nginx事件模組結構體的詳解

本文首先會講解事件模組相關的幾個模組定義及其執行流程進行講解,其原始碼的講解將會在後面的文章中進行。

推薦教學:Nginx教學

nginx的事件核心模組主要有兩個:ngx_events_modulengx_event_core_module。這兩個模組的主要差異在於,ngx_events_module的類型為NGX_CORE_MODULE,其本質上雖然是核心模組類型,但是卻是事件模組的一個驅動點,其解析的配置項為events {},並且會建立用於儲存事件模組相關配置的結構體;

而ngx_event_core_module的類型則是NGX_EVENT_MODULE,這個模組的配置物件中會儲存事件模組的基礎配置對象,其主要作用是解析events{}配置區塊中的子配置項。

下面我們就來看看這兩個模組的基礎配置。

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的對應位置。

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_tngx_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进程执行前的初始化工作。

3. 模块方法的执行流程

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

關於nginx事件模組結構體的詳解

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

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

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

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

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

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

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

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

以上是關於nginx事件模組結構體的詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:oschina.net。如有侵權,請聯絡admin@php.cn刪除