您好,登錄后才能下訂單哦!
nginx中怎么實現一個事件模塊,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
nginx在解析nginx.conf配置文件時,如果當前解析的配置項名稱為events
,并且是一個配置塊,則會調用ngx_events_block()
方法解析該配置塊,如下是該方法的源碼:
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"; } // 這里主要是計算event模塊的個數,并且將各個event模塊的相對順序標記在了該模塊的ctx_index屬性中 ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_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; } // 將數組值賦值到conf中,也即關聯到核心配置對象ngx_cycle_t中 *(void **) conf = ctx; 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; } } } // 這里將*cf結構體進行了復制,臨時存儲在pcf中,然后初始化當前的*cf結構體的模塊相關的參數, // 以進行下一步的解析 pcf = *cf; cf->ctx = ctx; cf->module_type = NGX_EVENT_MODULE; cf->cmd_type = NGX_EVENT_CONF; // 解析events{}配置塊中的子配置項 rv = ngx_conf_parse(cf, NULL); // 重新將pcf復制給*cf,以供后面返回使用 *cf = pcf; if (rv != NGX_CONF_OK) { return rv; } // 到這里,說明events{}配置塊的配置項都解析完成了,因而這里調用各個模塊的init_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; // 如果當前模塊的init_conf()不為空,則調用其init_conf()方法初始化配置項 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_events_block()
方法主要完成的工作有如下幾個:
調用ngx_count_modules()
方法對事件模塊序號進行標記,需要注意的是,這里的排序是針對當前模塊在所有事件類型模塊中的順序進行標記,并且將序號保存在各模塊的ctx_index
屬性中,比如這里的事件類型核心模塊ngx_event_core_module
的ctx_index
就為0;
為指針ctx
申請內存空間,并且申請一個數組,將其地址賦值給ctx
指針,這里的數組長度就為事件模塊的數目。其實這里的數組就是用來保存每個事件模塊的配置對象的,當前事件模塊在所有事件模塊中的相對位置就對應于該數組中的相對位置,這里的相對位置也即前一步中計算得到的ctx_index
;
調用各個事件模塊的create_conf()
方法創建各自的配置結構體,并且將其保存在ctx
指針指向的數組中;
調用ngx_conf_parse()
方法對配置文件繼續解析,前面我們已經講到,ngx_events_block()
方法就是解析到events
配置項的時候才調用的,因而這里的ngx_conf_parse()
方法的調用就是繼續解析events
配置塊的子配置項,而該方法調用完成則說明events
配置塊里的配置項都已經解析完成;
調用各個模塊的init_conf()
方法對配置項進行初始化,簡單的說,就是,由于在nginx.conf中只配置了部分配置項的值,而剩余的配置項就由init_conf()
方法來設置默認值;
在nginx解析完nginx.conf配置文件的所有配置項后(包括前一步中講解的對events配置項的解析),其就會調用所有核心模塊的init_conf()
方法對核心模塊的配置項進行初始化。這里的核心模塊就包括ngx_events_module
,該模塊的init_conf()
方法指向的就是這里的ngx_event_init_conf()
方法,該方法本質上并沒有做什么工作,只是檢查了是否創建了存儲事件模塊配置項的結構體數組。如下是ngx_event_init_conf()
方法的源碼:
static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf) { if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no \"events\" section in configuration"); return NGX_CONF_ERROR; } return NGX_CONF_OK; }
上面兩個方法就是ngx_events_module
核心模塊的兩個主要的配置方法,可以看到,這個核心模塊的主要作用就是創建了一個數組,用于存儲各個事件模塊的配置結構體的。下面我們來看一下事件核心模塊的主要方法。
在第1點中我們講到,解析events
配置塊的子配置項之前,會調用各個事件模塊的create_conf()
方法來創建其使用的存儲配置數據的結構體,而后調用ngx_conf_parse()
方法來解析子配置項,接著調用各個事件模塊的init_conf()
方法初始化各個模塊配置數據的結構體。這里ngx_event_core_module_ctx
就是一個事件類型的模塊,其create_conf
屬性指向的就是ngx_event_core_create_conf()
方法,而init_conf
屬性指向的就是ngx_event_core_init_conf()
方法。這一節我們首先講解ngx_event_core_create_conf()
方法的實現原理:
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_event_core_create_conf()
方法本質上就是創建了一個ngx_event_conf_t
結構體,并且將各個屬性都設置為未設置狀態。
前面我們講到,在解析完各個子配置項之后,nginx會調用各個事件模塊的init_conf()
方法,這里的核心事件模塊就是這個ngx_event_core_init_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) int fd; #endif ngx_int_t i; ngx_module_t *module; ngx_event_module_t *event_module; module = NULL; #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL) // 測試是否具有創建epoll句柄的權限 fd = epoll_create(100); if (fd != -1) { // 關閉創建的epoll句柄,并且將module指向epoll模塊 (void) close(fd); module = &ngx_epoll_module; } else if (ngx_errno != NGX_ENOSYS) { module = &ngx_epoll_module; } #endif // 這里,如果沒有前面判斷的模塊類型,則默認使用事件模塊中的第一個模塊作為事件處理模型 if (module == NULL) { for (i = 0; cycle->modules[i]; i++) { if (cycle->modules[i]->type != NGX_EVENT_MODULE) { continue; } event_module = cycle->modules[i]->ctx; if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0) { continue; } module = cycle->modules[i]; break; } } // 如果此時module還是為NULL,則返回異常 if (module == NULL) { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found"); return NGX_CONF_ERROR; } // 下面的操作主要是判斷各個屬性是否為初始設置的無效值,如果是,則說明nginx.conf中沒有配置 // 關于該屬性的配置項,那么這里就會為該屬性設置默認值 ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS); cycle->connection_n = ecf->connections; 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); ngx_conf_init_value(ecf->multi_accept, 0); ngx_conf_init_value(ecf->accept_mutex, 0); ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500); return NGX_CONF_OK; }
ngx_event_core_init_conf()
方法的主要做了兩件事:
選擇當前所使用的模塊,如果沒指定,則默認使用第一個事件模塊;
初始化事件核心模塊的配置結構體的各個屬性值為默認值。
對于ngx_event_core_module
模塊而言,其還指定了兩個方法,一個是用于初始化模塊的ngx_event_module_init()
方法,另一個是用于worker進程執行主循環邏輯之前進行調用的ngx_event_process_init()
方法。ngx_event_module_init()
方法是在master進程中調用的,其會在解析完nginx.conf文件中的所有配置項之后調用,本質上,該方法的作用就是對當前配置的核心模塊(事件模塊)進行初始化。如下是ngx_event_module_init()
方法的源碼:
/** * 當前方法的主要作用是申請一塊用于存儲統計數據的共享內存,然后設置ngx_accept_mutex_ptr、 * ngx_connection_counter、ngx_temp_number等變量的地址,如果開啟了slab stat, * 那么還會設置ngx_stat_accepted、ngx_stat_handled、ngx_stat_requests等的地址,以統計更多的數據 */ static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle) { void ***cf; u_char *shared; size_t size, cl; ngx_shm_t shm; ngx_time_t *tp; ngx_core_conf_t *ccf; ngx_event_conf_t *ecf; // 獲取core event module的配置結構體 cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module); ecf = (*cf)[ngx_event_core_module.ctx_index]; if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "using the \"%s\" event method", ecf->name); } // 獲取core module的配置對象 ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_timer_resolution = ccf->timer_resolution; #if !(NGX_WIN32) { ngx_int_t limit; struct rlimit rlmt; if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "getrlimit(RLIMIT_NOFILE) failed, ignored"); } else { // 這里主要是檢查當前事件模塊配置的connections數目是否超過了操作系統限制的最大文件句柄數, // 或者超過了配置文件中指定的最大文件句柄數 if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur && (ccf->rlimit_nofile == NGX_CONF_UNSET || ecf->connections > (ngx_uint_t) ccf->rlimit_nofile)) { limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ? (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile; ngx_log_error(NGX_LOG_WARN, cycle->log, 0, "%ui worker_connections exceed " "open file resource limit: %i", ecf->connections, limit); } } } #endif /* !(NGX_WIN32) */ if (ccf->master == 0) { return NGX_OK; } if (ngx_accept_mutex_ptr) { return NGX_OK; } /* cl should be equal to or greater than cache line size */ cl = 128; size = cl /* ngx_accept_mutex */ + cl /* ngx_connection_counter */ + cl; /* ngx_temp_number */ #if (NGX_STAT_STUB) size += cl /* ngx_stat_accepted */ + cl /* ngx_stat_handled */ + cl /* ngx_stat_requests */ + cl /* ngx_stat_active */ + cl /* ngx_stat_reading */ + cl /* ngx_stat_writing */ + cl; /* ngx_stat_waiting */ #endif // 設置共享內存的大小 shm.size = size; ngx_str_set(&shm.name, "nginx_shared_zone"); shm.log = cycle->log; // 為共享內存結構體申請內存塊 if (ngx_shm_alloc(&shm) != NGX_OK) { return NGX_ERROR; } // addr就是申請的共享內存塊的地址 shared = shm.addr; ngx_accept_mutex_ptr = (ngx_atomic_t *) shared; ngx_accept_mutex.spin = (ngx_uint_t) -1; if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared, cycle->lock_file.data) != NGX_OK) { return NGX_ERROR; } // 獲取ngx_connection_counter的地址 ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl); // 將ngx_connection_counter的值設置為1 (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "counter: %p, %uA", ngx_connection_counter, *ngx_connection_counter); // 獲取ngx_temp_number的地址 ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl); tp = ngx_timeofday(); // 生成一個隨機數 ngx_random_number = (tp->msec << 16) + ngx_pid; #if (NGX_STAT_STUB) ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl); ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl); ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl); ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl); ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl); ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl); ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl); #endif return NGX_OK; }
ngx_event_module_init()
方法主要完成的工作有如下幾個:
獲取配置的timer_resolution屬性值,并將其賦值給ngx_timer_resolution屬性,這個屬性的作用主要是指定更新nginx緩存的時間的定時任務的執行時間間隔;
獲取nginx配置的文件描述符和當前操作系統的文件描述符的配置,對比兩個值,從而更新當前進程所能開啟的文件描述符的個數;
聲明一塊共享內存,用于存儲nginx進行統計用的各個屬性的數據。
ngx_event_process_init()
方法主要是在worker進程執行主循環之前進行初始化調用的,如下是該方法的源碼:
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) { ngx_uint_t m, i; ngx_event_t *rev, *wev; ngx_listening_t *ls; ngx_connection_t *c, *next, *old; ngx_core_conf_t *ccf; ngx_event_conf_t *ecf; ngx_event_module_t *module; // 獲取核心模塊的配置對象 ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); // 獲取事件核心模塊的配置對象 ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); // 判斷當前如果滿足三個條件,則標記當前為使用共享鎖的方式: // 1. 當前為master-worker模式; // 2. 當前worker進程的數量大于1; // 3. 當前打開了使用共享鎖的開關; if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) { ngx_use_accept_mutex = 1; ngx_accept_mutex_held = 0; ngx_accept_mutex_delay = ecf->accept_mutex_delay; } else { // 如果不滿足上述條件,則指定不使用共享鎖 ngx_use_accept_mutex = 0; } #if (NGX_WIN32) /* * disable accept mutex on win32 as it may cause deadlock if * grabbed by a process which can't accept connections */ ngx_use_accept_mutex = 0; #endif // 這里這兩個隊列的主要作用在于,每個worker進程在獲取到共享鎖之后,就會接收客戶端accept事件, // 然后將其放入到ngx_posted_accept_events隊列中,接著處理該隊列中的事件,并且將客戶端連接添加到 // ngx_posted_events隊列中,然后再釋放鎖,也就是說獲取鎖的worker進程只需要進行accept客戶端連接, // 然后將鎖的權限交給其他的進程,并且再自行處理接收到的連接的讀寫事件 // 創建ngx_posted_accept_events隊列,該隊列用于接收客戶端的連接事件 ngx_queue_init(&ngx_posted_accept_events); // 創建ngx_posted_events隊列,該隊列用于處理客戶端連接的讀寫事件 ngx_queue_init(&ngx_posted_events); // 初始化一個用于存儲事件的紅黑樹 if (ngx_event_timer_init(cycle->log) == NGX_ERROR) { return NGX_ERROR; } for (m = 0; cycle->modules[m]; m++) { if (cycle->modules[m]->type != NGX_EVENT_MODULE) { continue; } // ecf->use存儲了所選用的事件模型的模塊序號,這里是找到該模塊 if (cycle->modules[m]->ctx_index != ecf->use) { continue; } // module即為所選用的事件模型對應的模塊 module = cycle->modules[m]->ctx; // 調用指定事件模型的初始化方法 if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) { /* fatal */ exit(2); } break; } #if !(NGX_WIN32) // ngx_timer_resolution表示發送更新時間事件的時間間隔 // 這里表示如果設置了ngx_timer_resolution,并且沒有設置定時事件。 // ngx_event_flags是在事件模塊的初始化中設置的,而且只有eventport和kqueue模型才會將 // NGX_USE_TIMER_EVENT設置到ngx_event_flags中 if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) { struct sigaction sa; struct itimerval itv; ngx_memzero(&sa, sizeof(struct sigaction)); // 這里的sa主要是添加下面的SIGALRM的信號監聽事件,該信號的作用是每隔一段時間就會向當前進程發出 // 當前進程收到信號之后就會調用下面的ngx_timer_signal_handler()方法,該方法中會將 // ngx_event_timer_alarm設置為1,而后當前進程在進行事件循環的時候,判斷如果 // ngx_event_timer_alarm為1,則會更新當前進程所緩存的時間數據 sa.sa_handler = ngx_timer_signal_handler; sigemptyset(&sa.sa_mask); // 添加SIGALRM監聽信號 if (sigaction(SIGALRM, &sa, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigaction(SIGALRM) failed"); return NGX_ERROR; } // 設置時間間隔相關參數 itv.it_interval.tv_sec = ngx_timer_resolution / 1000; itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000; itv.it_value.tv_sec = ngx_timer_resolution / 1000; itv.it_value.tv_usec = (ngx_timer_resolution % 1000) * 1000; // 按照指定的時間間隔設置定時器 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setitimer() failed"); } } // NGX_USE_FD_EVENT表示event filter沒有透明數據,并需要一個文件描述符表,其主要用于poll、/dev/poll if (ngx_event_flags & NGX_USE_FD_EVENT) { struct rlimit rlmt; if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "getrlimit(RLIMIT_NOFILE) failed"); return NGX_ERROR; } // 這里主要是初始化最大個數的ngx_connection_t結構體,將其保存在files數組中 cycle->files_n = (ngx_uint_t) rlmt.rlim_cur; cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n, cycle->log); if (cycle->files == NULL) { return NGX_ERROR; } } #else if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) { ngx_log_error(NGX_LOG_WARN, cycle->log, 0, "the \"timer_resolution\" directive is not supported " "with the configured event method, ignored"); ngx_timer_resolution = 0; } #endif // 申請指定個數的ngx_connection_t數組,這里的connection_n對應的是配置 // 文件中的worker_connections所指定的大小 cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log); if (cycle->connections == NULL) { return NGX_ERROR; } c = cycle->connections; // 申請指定個數的ngx_event_t數組,其長度與connections數組一致, // 這樣便可以將connections數組與read_events數組進行對應 cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log); if (cycle->read_events == NULL) { return NGX_ERROR; } rev = cycle->read_events; for (i = 0; i < cycle->connection_n; i++) { rev[i].closed = 1; // 初始狀態默認讀事件都是closed狀態 rev[i].instance = 1; // 初始時初始化instance為1 } // 申請指定個數的ngx_event_t數組,其長度與connections數組一致, // 這樣便可以將connections數組與write_events數組進行對應 cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log); if (cycle->write_events == NULL) { return NGX_ERROR; } wev = cycle->write_events; for (i = 0; i < cycle->connection_n; i++) { wev[i].closed = 1; // 初始時寫事件默認也都是closed狀態 } i = cycle->connection_n; next = NULL; do { i--; // 將read_events和write_events數組的元素依次賦值到connections數組元素的read和write屬性中, // 并且將connections數組組裝成一個單鏈表 c[i].data = next; c[i].read = &cycle->read_events[i]; c[i].write = &cycle->write_events[i]; c[i].fd = (ngx_socket_t) -1; next = &c[i]; } while (i); // 初始狀態時,所有的connections都未被使用,因而需要存儲在free_connections鏈表中 cycle->free_connections = next; cycle->free_connection_n = cycle->connection_n; /* for each listening socket */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { #if (NGX_HAVE_REUSEPORT) if (ls[i].reuseport && ls[i].worker != ngx_worker) { continue; } #endif // 這里是為當前所監聽的每一個端口都綁定一個ngx_connection_t結構體 c = ngx_get_connection(ls[i].fd, cycle->log); if (c == NULL) { return NGX_ERROR; } c->type = ls[i].type; c->log = &ls[i].log; c->listening = &ls[i]; ls[i].connection = c; rev = c->read; rev->log = c->log; // 標記accept為1,表示當前可以接收客戶端的連接事件 rev->accept = 1; #if (NGX_HAVE_DEFERRED_ACCEPT) rev->deferred_accept = ls[i].deferred_accept; #endif if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) { if (ls[i].previous) { /* * delete the old accept events that were bound to * the old cycle read events array */ // 刪除舊的事件 old = ls[i].previous->connection; if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) { return NGX_ERROR; } old->fd = (ngx_socket_t) -1; } } #if (NGX_WIN32) if (ngx_event_flags & NGX_USE_IOCP_EVENT) { ngx_iocp_conf_t *iocpcf; rev->handler = ngx_event_acceptex; if (ngx_use_accept_mutex) { continue; } if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) { return NGX_ERROR; } ls[i].log.handler = ngx_acceptex_log_error; iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module); if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex) == NGX_ERROR) { return NGX_ERROR; } } else { rev->handler = ngx_event_accept; if (ngx_use_accept_mutex) { continue; } if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } } #else // SOCK_STREAM表示TCP,一般都是TCP,也就是說在接收到客戶端的accept事件之后, // 就會調用ngx_event_accept()方法處理該事件 rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept : ngx_event_recvmsg; #if (NGX_HAVE_REUSEPORT) // 添加當前事件到事件監聽隊列中 if (ls[i].reuseport) { if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } continue; } #endif if (ngx_use_accept_mutex) { continue; } #if (NGX_HAVE_EPOLLEXCLUSIVE) if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ccf->worker_processes > 1) { if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT) == NGX_ERROR) { return NGX_ERROR; } continue; } #endif // 添加當前事件到事件監聽隊列中 if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } #endif } return NGX_OK; }
這里ngx_event_process_init()
方法主要完成了如下幾個工作:
根據所使用的進程模式和worker進程數量,配置是否使用共享鎖的字段ngx_use_accept_mutex;
初始化用于處理事件的ngx_posted_accept_events隊列和ngx_posted_events隊列;
調用當前所使用的事件模型模塊的init()
方法,比如epoll模型在該init()
方法中就會構建一個epoll句柄,以便后續往其中添加需要監聽的事件;
判斷是否配置了ngx_timer_resolution屬性,也即上一節中獲取的更新nginx緩存時間的定時任務的執行頻率字段,如果配置了,則創建一個定時任務以定時設置ngx_event_timer_alarm屬性值;
創建長度相同的connections
、read_events
和write_events
數組,并且將connections
數組中每個ngx_connection_t
結構體的read
屬性指向read_events
數組中對應位置的讀事件結構體,將write
屬性指向write_events
數組中對應位置的寫事件結構體,并且將所有的connections
組織成單鏈表存儲到ngx_cycle_t的free_connections屬性中;
為當前nginx監聽的各個端口配置一個ngx_connection_t
結構體,并且為其添加對應的事件監聽器,以等待客戶端連接的到來。
看完上述內容,你們掌握nginx中怎么實現一個事件模塊的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。