nginx 事件模块配置解析(1) - ngx_events_block

2018-07-27 10:23:26

当在配置文件里遇到 events{} 块时,就会调用 ngx_events_block() 函数来初始化。只要是初始化2个 NGX_EVENT_MODULE 模块,在 linux 上一般为 ngx_event_core_module 和 ngx_epoll_module。

// 解析events配置块
// 设置事件模块的ctx_index
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;

    // 不允许出现两个events配置块
    if (*(void **) conf) {
        return "is duplicate";
    }

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

    // 得到所有的事件模块数量,一般为2个
    // 设置事件模块的ctx_index
    // 0-ngx_event_core_module
    // 1-ngx_epoll_module
    ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);

    // 分配一个指针的空间,主要是跟 http 的风格同一
    // 相当于 ngx_http_conf_ctx_t 只有一个 **main_conf成员,
    ctx = ngx_pcalloc(cf->pool, sizeof(void *));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    // 让上面分配的第一个指针(其实也是唯一的指针)指向存有2个指针的数组
    *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
    if (*ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    // 在cycle里存储这个指针
    *(void **) conf = ctx;

    // 对每一个事件模块调用create_conf创建配置结构体
    // 事件模块的层次很简单,没有多级,所以二维数组就够了
    for (i = 0; cf->cycle->modules[i]; i++) {
        if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }

        m = cf->cycle->modules[i]->ctx;

        // 调用create_conf创建配置结构体
        if (m->create_conf) {
            (*ctx)[cf->cycle->modules[i]->ctx_index] =
                                                     m->create_conf(cf->cycle);
            if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }

    // 暂存当前的解析上下文
    pcf = *cf;

    // 设置事件模块的新解析上下文
    cf->ctx = ctx;
    cf->module_type = NGX_EVENT_MODULE;
    cf->cmd_type = NGX_EVENT_CONF;

    // 递归解析事件相关模块
    rv = ngx_conf_parse(cf, NULL);

    // 恢复之前保存的解析上下文
    *cf = pcf;

    if (rv != NGX_CONF_OK) {
        return rv;
    }

    // 解析完毕,需要初始化配置,即给默认值
    for (i = 0; cf->cycle->modules[i]; i++) {
        if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }

        m = cf->cycle->modules[i]->ctx;

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

    return NGX_CONF_OK;
}

ngx_event_core_moudle 模块的 create_conf() 函数如下,主要是创建 ngx_event_conf_t 结构体。

// event_core模块的配置结构体
typedef struct {
    // nginx每个进程可使用的连接数量,即cycle里的连接池大小
    ngx_uint_t    connections;

    // 使用的是哪个event模块,值是具体事件模块的ctx_index
    ngx_uint_t    use;

    // 是否尽可能多接受客户端请求,会影响进程间负载均衡
    ngx_flag_t    multi_accept;

    // 是否使用负载均衡锁,在共享内存里的一个原子变量
    ngx_flag_t    accept_mutex;

    // 负载均衡锁的等待时间,进程如果未获得锁会等一下再尝试
    ngx_msec_t    accept_mutex_delay;

    // 事件模块的名字,如epoll/select/kqueue
    // 使用name在event模块里查找,决定使用的事件机制
    u_char       *name;

} ngx_event_conf_t;

// 创建event_core模块的配置结构体,成员初始化为unset
static void *
ngx_event_core_create_conf(ngx_cycle_t *cycle){
    ngx_event_conf_t  *ecf;

    ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
    if (ecf == NULL) {
        return NULL;
    }

    ecf->connections = NGX_CONF_UNSET_UINT;
    ecf->use = NGX_CONF_UNSET_UINT;
    ecf->multi_accept = NGX_CONF_UNSET;
    ecf->accept_mutex = NGX_CONF_UNSET;
    ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
    ecf->name = (void *) NGX_CONF_UNSET;

    return ecf;
}

而 ngx_epoll_module 模块的 create_conf(),主要是创建 ngx_epoll_conf_t 结构体。

// epoll模块的配置结构体
typedef struct {
    // epoll系统调用,获取事件的数组大小
    // 对应指令epoll_events
    ngx_uint_t  events;

    // aio暂不研究
    ngx_uint_t  aio_requests;
} ngx_epoll_conf_t;

// 创建配置结构体
static void *
ngx_epoll_create_conf(ngx_cycle_t *cycle){
    ngx_epoll_conf_t  *epcf;

    epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t));
    if (epcf == NULL) {
        return NULL;
    }

    // 两个值都是数字,所以要置为-1
    epcf->events = NGX_CONF_UNSET;
    epcf->aio_requests = NGX_CONF_UNSET;

    return epcf;
}


接下来就是 ngx_event_core_module 模块的初始化赋值。

// 所有模块配置解析完毕后,对配置进行初始化
// 如果有的指令没有写,就要给正确的默认值
// 模块默认使用epoll
// 默认不接受多个请求,也就是一次只accept一个连接
// 1.11.3之前默认使用负载均衡锁,之后默认关闭
static char *
ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
{
    ngx_event_conf_t  *ecf = conf;

    // rtsig在nginx 1.9.x已经删除

    ngx_int_t            i;
    ngx_module_t        *module;
    ngx_event_module_t  *event_module;
    
    //为了代码简洁,这里省略了很多判断,直接使用 epoll。
    module = &ngx_epoll_module;

    // nginx每个进程可使用的连接数量,即cycle里的连接池大小默认为512
    ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);

    // 如果没有使用worker_connections指令,在这里设置
    cycle->connection_n = ecf->connections;

    // 决定使用的事件模型,之前的module只作为默认值,如果已经使用了use则无效
    ngx_conf_init_uint_value(ecf->use, module->ctx_index);

    // 初始化使用的事件模块的名字
    event_module = module->ctx;
    ngx_conf_init_ptr_value(ecf->name, event_module->name->data);

    // 默认不接受多个请求,也就是一次只accept一个连接
    ngx_conf_init_value(ecf->multi_accept, 0);

    // 1.11.3之前默认使用负载均衡锁,之后默认关闭
    ngx_conf_init_value(ecf->accept_mutex, 0);

    // 默认负载均衡锁的等待时间是500毫秒
    ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);

    return NGX_CONF_OK;
}

下面是ngx_epoll_module 模块的初始化赋值

// 初始化配置结构体
static char *
ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf){
    ngx_epoll_conf_t *epcf = conf;

    // 如果不使用epoll_events指令, epcf->events默认是512
    ngx_conf_init_uint_value(epcf->events, 512);

    ngx_conf_init_uint_value(epcf->aio_requests, 32);

    return NGX_CONF_OK;
}

内存大致如下图


备注

1.测试环境centos7 64位,nginx版本为 1.14.0。
2..原文地址http://www.freecls.com/a/2712/db


©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。