nginx網絡通訊模塊設計與實現分析

nginx做爲後端程序不可缺乏的中間件,憑藉其優異的性能和穩定,得到了大量的用戶nginx

目錄結構

nginx的文件目錄很是清晰,雖然有6個目錄,可是能夠歸成兩類,core目錄存放 nginx生命週期相關的函數,好比master、worker進程的建立,模塊構造函數調用,核心數據結構和算法實現

其它目錄主要就是存放nginx功能的擴展了、nginx幾乎全部的功能都是經過擴展實現的git

入口程序

src/core/nginx.c::main()github

在入口程序中,nginx主要是處理了命令行參數,若是沒有帶參數運行nginx,則啓動nginx服務器ngx_master_process_cycle(cycle算法

核心調用以下後端

  1. ngx_strerror_init() 初始化錯誤碼存放鏈表
  2. ngx_get_options(argc, argv) 將命令行參數解析到全局變量
  3. 初始化日誌系統: ngx_log_init(ngx_prefix)
  4. ngx_save_argv(&init_cycle, argc, argv) 保存全部命令行參數到全局變量
  5. ngx_process_options(&init_cycle)

給啓用的nginx模塊進行編號服務器

ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
    ngx_modules[i]->index = ngx_max_module++;
}
複製代碼
  1. ngx_init_cycle(&init_cycle) 初始化cycle全局變量
  2. ngx_signal_process(cycle, ngx_signal) 對nginx master發送信號kill()
ngx_int_t
ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid)
{
    ngx_signal_t  *sig;

    for (sig = signals; sig->signo != 0; sig++) {
        if (ngx_strcmp(name, sig->name) == 0) {
            if (kill(pid, sig->signo) != -1) {
                return 0;
            }

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "kill(%P, %d) failed", pid, sig->signo);
        }
    }

    return 1;
}
複製代碼
  1. 建立pidfile ngx_create_pidfile(&ccf->pid, cycle->log)
  2. 啓動nginx master進程 ngx_master_process_cycle(cycle)

模塊/鉤子機制

nginx的模塊是經過在nginx運行的各個生命週期調用模塊的鉤子函數實現的 拿src/event/ngx_event.c:184舉例網絡

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
};
複製代碼

能夠看到nginx爲每一個模塊都支持了7個鉤子函數數據結構

回調函數 做用
init master master進程建立時調用
init module 初始化模塊時調用
init process worker進程初始化時調用
init thread 未使用
exit thread 未使用
exit process worker進程退出時調用
exit master master進程退出時調用

事件驅動選擇

nginx的事件驅動在src/event/ngx_event.c::ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)函數中選擇,核心代碼以下數據結構和算法

static char * ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) {
    ngx_event_conf_t  *ecf = conf;
    ...
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)

    fd = epoll_create(100);

    if (fd != -1) {
        close(fd);
        module = &ngx_epoll_module;

    } else if (ngx_errno != NGX_ENOSYS) {
        module = &ngx_epoll_module;
    }

#endif
    ...
    ngx_conf_init_uint_value(ecf->use, module->ctx_index);
    ...
}
複製代碼

能夠看到nginx默認會優先選擇epoll網絡事件模型進行驅動,日後纔是kqueue、select,而後把最後肯定使用的網絡驅動模塊的索引存到ecf->use這個指針中,後面就能夠經過這個use指針調用nginx使用的網絡庫了ide

事件初始化函數調用

在worker進程初始化時:src/event/ngx_event.c:ngx_event_process_init()

for (m = 0; ngx_modules[m]; m++) {
    if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
        continue;
    }

    if (ngx_modules[m]->ctx_index != ecf->use) {
        continue;
    }

    module = ngx_modules[m]->ctx;

    if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
        /* fatal */
        exit(2);
    }

    break;
}
複製代碼

遍歷全部模塊,找出目前系統選擇的網絡庫ngx_modules[m]->ctx_index != ecf->use,而後調用初始化函數進行初始化module->actions.init(cycle, ngx_timer_resolution)

actions結構體在src/event/ngx_event.h:ngx_event_actions_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  (*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;
複製代碼

初始化在src/event/modules/ngx_epoll_module.c:ngx_event_module_t ngx_epoll_module_ctx

ngx_event_module_t  ngx_epoll_module_ctx = {
    &epoll_name,
    ngx_epoll_create_conf,               /* create configuration */
    ngx_epoll_init_conf,                 /* init configuration */

    {
        ngx_epoll_add_event,             /* add an event */
        ngx_epoll_del_event,             /* delete an event */
        ngx_epoll_add_event,             /* enable an event */
        ngx_epoll_del_event,             /* disable an event */
        ngx_epoll_add_connection,        /* add an connection */
        ngx_epoll_del_connection,        /* delete an connection */
        NULL,                            /* process the changes */
        ngx_epoll_process_events,        /* process the events */
        ngx_epoll_init,                  /* init the events */
        ngx_epoll_done,                  /* done the events */
    }
};
複製代碼

init函數指針對應調用的就是ngx_epoll_init函數了

static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
    ...
    ep = epoll_create(cycle->connection_n / 2);
    ...
}
複製代碼

能夠看到主要是調用epoll_create函數了,其他的網絡庫調用能夠經過ngx_event_actions_t、ngx_epoll_module_ctx找到了

一些注意的點

本文基於nginx 1.2.0 寫成,源碼下載參考:nginx.org/download/

參考資料

  1. Inside NGINX: How We Designed for Performance & Scale
  2. github.com/Toudsour/ng…
相關文章
相關標籤/搜索