<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Nginx(七):location的使用以及nginx優(yōu)雅停機原理

          共 32679字,需瀏覽 66分鐘

           ·

          2021-02-27 14:23

          走過路過不要錯過

          點擊藍(lán)字關(guān)注我們


          上一篇中,我們了解了如何nginx的配置原則及解析框架,以及解析location配置的具體實現(xiàn),相信大家對該部分已經(jīng)有了比較深刻的認(rèn)識。

          本篇,我們進(jìn)一步來了解下,解析之后的配置,如何應(yīng)用到實際中的吧。當(dāng)然,我們只講解 location 的查找過程。

          1:location的接入流程

          在nginx的前幾篇中,我們已經(jīng)了解了,nginx對于網(wǎng)絡(luò)的請求接入過程,是一個基于事件的io模型,這是其高性能的根本。io接入之后,再通過 accept -> read -> init_http -> wait_request -> process_request_line -> process_request_header -> process_request ... 的過程,然后就是具體的處理實現(xiàn)。

          而對于location的處理,則是在 ngx_http_handler() 接入之后的分發(fā)工作。

          // http/ngx_http_core_module.c    voidngx_http_handler(ngx_http_request_t *r){    ngx_http_core_main_conf_t  *cmcf;
          r->connection->log->action = NULL;
          if (!r->internal) { switch (r->headers_in.connection_type) { case 0: r->keepalive = (r->http_version > NGX_HTTP_VERSION_10); break;
          case NGX_HTTP_CONNECTION_CLOSE: r->keepalive = 0; break;
          case NGX_HTTP_CONNECTION_KEEP_ALIVE: r->keepalive = 1; break; }
          r->lingering_close = (r->headers_in.content_length_n > 0 || r->headers_in.chunked); r->phase_handler = 0;
          } else { cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); r->phase_handler = cmcf->phase_engine.server_rewrite_index; }
          r->valid_location = 1;#if (NGX_HTTP_GZIP) r->gzip_tested = 0; r->gzip_ok = 0; r->gzip_vary = 0;#endif
          r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r);}
          // http/ngx_http_core_module.cvoidngx_http_core_run_phases(ngx_http_request_t *r){ ngx_int_t rc; ngx_http_phase_handler_t *ph; ngx_http_core_main_conf_t *cmcf;
          cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
          ph = cmcf->phase_engine.handlers; // 依次遍歷各checker, 直到有一個可以處理 while (ph[r->phase_handler].checker) {
          rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
          if (rc == NGX_OK) { return; } }}
          ngx_int_tngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){ u_char *p; size_t len; ngx_int_t rc; ngx_http_core_loc_conf_t *clcf;
          r->content_handler = NULL; r->uri_changed = 0; // 查找location的實現(xiàn),接入查找流程 rc = ngx_http_core_find_location(r);
          if (rc == NGX_ERROR) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; }
          clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
          if (!r->internal && clcf->internal) { ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); return NGX_OK; }
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "using configuration \"%s%V\"", (clcf->noname ? "*" : (clcf->exact_match ? "=" : "")), &clcf->name); // 將查找到的信息,更新到當(dāng)前會話中 ngx_http_update_location_config(r);
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http cl:%O max:%O", r->headers_in.content_length_n, clcf->client_max_body_size);
          if (r->headers_in.content_length_n != -1 && !r->discard_body && clcf->client_max_body_size && clcf->client_max_body_size < r->headers_in.content_length_n) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client intended to send too large body: %O bytes", r->headers_in.content_length_n);
          r->expect_tested = 1; (void) ngx_http_discard_request_body(r); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE); return NGX_OK; }
          if (rc == NGX_DONE) { ngx_http_clear_location(r);
          r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; }
          r->headers_out.location->hash = 1; ngx_str_set(&r->headers_out.location->key, "Location");
          if (r->args.len == 0) { r->headers_out.location->value = clcf->name;
          } else { len = clcf->name.len + 1 + r->args.len; p = ngx_pnalloc(r->pool, len);
          if (p == NULL) { ngx_http_clear_location(r); ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; }
          r->headers_out.location->value.len = len; r->headers_out.location->value.data = p;
          p = ngx_cpymem(p, clcf->name.data, clcf->name.len); *p++ = '?'; ngx_memcpy(p, r->args.data, r->args.len); }
          ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY); return NGX_OK; }
          r->phase_handler++; return NGX_AGAIN;}

          以上就是location的接入過程,主要就是接受http_handler的分配處理,具體如何進(jìn)行查找.即 location 的處理是在nginx讀取完所有的請求體之后,依次處理的其中一個步驟。我們下節(jié)再看。

          2. location的查找過程

          上節(jié)看到,http模塊在處理location時,使用一個ngx_http_core_find_location()封裝好了其查找過程。想想其實現(xiàn),應(yīng)該差不多就是依次匹配原來解析出的信息,當(dāng)然這里面應(yīng)該是有各種優(yōu)先級的體現(xiàn)。

          // http/ngx_http_core_module.cstatic ngx_int_tngx_http_core_find_location(ngx_http_request_t *r){    ngx_int_t                  rc;    ngx_http_core_loc_conf_t  *pclcf;#if (NGX_PCRE)    ngx_int_t                  n;    ngx_uint_t                 noregex;    ngx_http_core_loc_conf_t  *clcf, **clcfp;
          noregex = 0;#endif
          pclcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); // 委托給 static_location 查找 rc = ngx_http_core_find_static_location(r, pclcf->static_locations);
          if (rc == NGX_AGAIN) {
          #if (NGX_PCRE) clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
          noregex = clcf->noregex;#endif
          /* look up nested locations */ // NGX_AGAIN, 則進(jìn)行多次嵌套查找,以保證最佳匹配 rc = ngx_http_core_find_location(r); } // 匹配成功,則返回,主要是針對'='的匹配 if (rc == NGX_OK || rc == NGX_DONE) { return rc; }
          /* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */
          #if (NGX_PCRE)
          if (noregex == 0 && pclcf->regex_locations) { // 正則匹配, 只要存在正則配置,那么正則匹配都會運行 // 相比于字符匹配,正則匹配性能更差 // 所以,當(dāng)你的正則配置越多,則查找效率則必然越差,沒必要配置正則就不要配了 for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) {
          ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "test location: ~ \"%V\"", &(*clcfp)->name); // 只要有一個正則匹配,則返回該配置 n = ngx_http_regex_exec(r, (*clcfp)->regex, &r->uri);
          if (n == NGX_OK) { // 符合正則表達(dá)式,則loc_conf應(yīng)用上去 r->loc_conf = (*clcfp)->loc_conf;
          /* look up nested locations */ // 與正常匹配相反,正則匹配是在一個匹配成功后,再進(jìn)入嵌套查詢 rc = ngx_http_core_find_location(r);
          return (rc == NGX_ERROR) ? rc : NGX_OK; }
          if (n == NGX_DECLINED) { continue; }
          return NGX_ERROR; } }#endif
          return rc;}
          // 非正則location 匹配查找過程/* * NGX_OK - exact match * NGX_DONE - auto redirect * NGX_AGAIN - inclusive match * NGX_DECLINED - no match */
          static ngx_int_tngx_http_core_find_static_location(ngx_http_request_t *r, ngx_http_location_tree_node_t *node){ u_char *uri; size_t len, n; ngx_int_t rc, rv;
          len = r->uri.len; uri = r->uri.data;
          rv = NGX_DECLINED;
          for ( ;; ) {
          if (node == NULL) { // node為null時,代表匹配完成,此時將返回之前最匹配的一個 loc_conf return rv; }
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "test location: \"%*s\"", (size_t) node->len, node->name); // 取小值進(jìn)行比較 n = (len <= (size_t) node->len) ? len : node->len; // 包含性檢查 rc = ngx_filename_cmp(uri, node->name, n);
          if (rc != 0) { // 二叉樹查找過程, 小于0在左,大于0在右 node = (rc < 0) ? node->left : node->right;
          continue; } // 相等的情況有兩種,第1種是本次uri 長于當(dāng)前配置的location // 第2種是本次uri 短于當(dāng)前配置的location // 針對第1種情況,是屬于一種完全匹配的 if (len > (size_t) node->len) {
          if (node->inclusive) {
          r->loc_conf = node->inclusive->loc_conf; rv = NGX_AGAIN; // 向前迭代匹配 node = node->tree; uri += n; len -= n;
          continue; }
          /* exact only */
          node = node->right;
          continue; } // 此為 uri >= location配置的情況 if (len == (size_t) node->len) {
          if (node->exact) { r->loc_conf = node->exact->loc_conf; return NGX_OK;
          } else { // 包含性匹配成功 r->loc_conf = node->inclusive->loc_conf; return NGX_AGAIN; } }
          /* len < node->len */ // 以'/'結(jié)尾的配置, 比uri 多一個值 if (len + 1 == (size_t) node->len && node->auto_redirect) {
          r->loc_conf = (node->exact) ? node->exact->loc_conf: node->inclusive->loc_conf; rv = NGX_DONE; }
          node = node->left; }}// 正則location查找// http/ngx_http_variable.cngx_int_tngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s){ ngx_int_t rc, index; ngx_uint_t i, n, len; ngx_http_variable_value_t *vv; ngx_http_core_main_conf_t *cmcf;
          cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
          if (re->ncaptures) { len = cmcf->ncaptures;
          if (r->captures == NULL || r->realloc_captures) { r->realloc_captures = 0;
          r->captures = ngx_palloc(r->pool, len * sizeof(int)); if (r->captures == NULL) { return NGX_ERROR; } }
          } else { len = 0; } // 正則匹配, pcre_exec rc = ngx_regex_exec(re->regex, s, r->captures, len); // 無匹配返回 NGX_DECLINED if (rc == NGX_REGEX_NO_MATCHED) { return NGX_DECLINED; }
          if (rc < 0) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"", rc, s, &re->name); return NGX_ERROR; }
          for (i = 0; i < re->nvariables; i++) {
          n = re->variables[i].capture; index = re->variables[i].index; vv = &r->variables[index];
          vv->len = r->captures[n + 1] - r->captures[n]; vv->valid = 1; vv->no_cacheable = 0; vv->not_found = 0; vv->data = &s->data[r->captures[n]];
          #if (NGX_DEBUG) { ngx_http_variable_t *v;
          v = cmcf->variables.elts;
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http regex set $%V to \"%v\"", &v[index].name, vv); }#endif }
          r->ncaptures = rc * 2; r->captures_data = s->data;
          return NGX_OK;}

          以上就是整個nginx非正則的location的匹配過程,可以看到其核心是使用一個有序二叉樹,進(jìn)行的快速查找過程,以盡可能多的匹配為準(zhǔn)。即 /api/a/b, /api/a 這兩個同時匹配的情況,則會選擇匹配最多的 /api/a/b 配置。借助于二叉樹的高效數(shù)據(jù)結(jié)構(gòu),其復(fù)雜度非常常低,O(lgn). 當(dāng)然,這個快速查找是依賴于其在解析配置時的良好數(shù)據(jù)維護(hù)。

          // http/ngx_http_core_module.c    ngx_int_tngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,    ngx_http_core_loc_conf_t *clcf){    ngx_http_location_queue_t  *lq;
          if (*locations == NULL) { *locations = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t)); if (*locations == NULL) { return NGX_ERROR; }
          ngx_queue_init(*locations); }
          lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t)); if (lq == NULL) { return NGX_ERROR; }
          if (clcf->exact_match#if (NGX_PCRE) || clcf->regex#endif || clcf->named || clcf->noname) { lq->exact = clcf; lq->inclusive = NULL;
          } else { lq->exact = NULL; lq->inclusive = clcf; }
          lq->name = &clcf->name; lq->file_name = cf->conf_file->file.name.data; lq->line = cf->conf_file->line; // 雖然看不懂在做什么,但是感覺很厲害的樣子 ngx_queue_init(&lq->list);
          ngx_queue_insert_tail(*locations, &lq->queue);
          return NGX_OK;}


          查找過程分解完畢,和nginx的官方文檔描述自然是一致的。優(yōu)先匹配 '=' 類的配置,其次會按照最長配置為原則查找,但正則配置的優(yōu)先級高于字符的匹配,沒必要不要隨意配置正則,因為正則會每次都全量查找。

          不過,因為這些所有的操作都是直接基于內(nèi)存的,并沒有io類的重量級操作,即使配置了幾百上千個location規(guī)則,性能也并不會有太大影響。但我們應(yīng)該要其根本原因。

          以上所說,僅是location的最外部匹配過程,但location本身是一個塊級的配置,它的內(nèi)部又有非常多的配置規(guī)則,這又要細(xì)化到其內(nèi)部解析了。

          3. nginx優(yōu)雅停機原理

          nginx進(jìn)程的控制與前面location使用有什么關(guān)系??當(dāng)然沒有關(guān)系了,只是想著也簡單,順便就一起講講了。

          一般的應(yīng)用進(jìn)程管理,只需使用系統(tǒng)提供的相關(guān)命令即可完成,比如 kill -9 <pid> 。而nginx專門提供了一些用于控制其進(jìn)程的方法,原因是其需要更優(yōu)雅地處理各種意外情況,如果直接使用系統(tǒng)的控制命令,會導(dǎo)致非常多的邊界問題,從而使得nginx本身不再完美。大家需要為這一個個的邊界問題,傷透了腦筋,這樣也許它就不再那么流行了。

          要實現(xiàn)優(yōu)雅停機,最本質(zhì)的工作是要實現(xiàn)資源的優(yōu)雅管理工作,但還有很重要的點是如何實現(xiàn)這管理工作的調(diào)用。來看看nginx如何處理:

          // core/ngx_cycle.cngx_int_tngx_signal_process(ngx_cycle_t *cycle, char *sig){    ssize_t           n;    ngx_pid_t         pid;    ngx_file_t        file;    ngx_core_conf_t  *ccf;    u_char            buf[NGX_INT64_LEN + 2];
          ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");
          ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
          ngx_memzero(&file, sizeof(ngx_file_t));
          file.name = ccf->pid; file.log = cycle->log;
          file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);
          if (file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, ngx_open_file_n " \"%s\" failed", file.name.data); return 1; }
          n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);
          if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file.name.data); }
          if (n == NGX_ERROR) { return 1; }
          while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }
          pid = ngx_atoi(buf, ++n);
          if (pid == (ngx_pid_t) NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "invalid PID number \"%*s\" in \"%s\"", n, buf, file.name.data); return 1; } // 以上解析pid, 驗證有效性, 下面進(jìn)行實際進(jìn)程管控 return ngx_os_signal_process(cycle, sig, pid);
          }
          // os/unix/ngx_process.cngx_int_tngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_pid_t pid){ ngx_signal_t *sig; // 遍歷所有控制指令,轉(zhuǎn)換成系統(tǒng)的控制標(biāo)識 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;}// 控制命令配置表ngx_signal_t signals[] = { { ngx_signal_value(NGX_RECONFIGURE_SIGNAL), // SIG##1 "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL), // SIG#HUP "reload", ngx_signal_handler },
          { ngx_signal_value(NGX_REOPEN_SIGNAL), // SIG##30 "SIG" ngx_value(NGX_REOPEN_SIGNAL), // SIG#USR1 "reopen", ngx_signal_handler },
          { ngx_signal_value(NGX_NOACCEPT_SIGNAL), // SIG##28 "SIG" ngx_value(NGX_NOACCEPT_SIGNAL), // SIG#WINCH "", ngx_signal_handler },
          { ngx_signal_value(NGX_TERMINATE_SIGNAL), // SIG##15 "SIG" ngx_value(NGX_TERMINATE_SIGNAL), // SIG#TERM "stop", ngx_signal_handler },
          { ngx_signal_value(NGX_SHUTDOWN_SIGNAL), // SIG##3 "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL), // SIG#QUIT "quit", ngx_signal_handler },
          { ngx_signal_value(NGX_CHANGEBIN_SIGNAL), // ##31 "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL), // USR2 "", ngx_signal_handler },
          { SIGALRM, "SIGALRM", "", ngx_signal_handler },
          { SIGINT, "SIGINT", "", ngx_signal_handler },
          { SIGIO, "SIGIO", "", ngx_signal_handler },
          { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
          { SIGSYS, "SIGSYS, SIG_IGN", "", NULL },
          { SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL },
          { 0, NULL, "", NULL }};// 以上命令的注冊過程如下, 以便在響應(yīng)時可處理ngx_int_tngx_init_signals(ngx_log_t *log){ ngx_signal_t *sig; struct sigaction sa;
          for (sig = signals; sig->signo != 0; sig++) { ngx_memzero(&sa, sizeof(struct sigaction));
          if (sig->handler) { sa.sa_sigaction = sig->handler; sa.sa_flags = SA_SIGINFO;
          } else { sa.sa_handler = SIG_IGN; }
          sigemptyset(&sa.sa_mask); if (sigaction(sig->signo, &sa, NULL) == -1) {#if (NGX_VALGRIND) ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "sigaction(%s) failed, ignored", sig->signame);#else ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "sigaction(%s) failed", sig->signame); return NGX_ERROR;#endif } }
          return NGX_OK;}

          以上控制指定的處理器,幾乎都被設(shè)置為 ngx_signal_handler,即當(dāng)nginx進(jìn)程收到控制信號,將會該用該方法進(jìn)行響應(yīng)。

          // 其處理邏輯如下: (主要就是根據(jù)當(dāng)前進(jìn)程的不同角色和控制信號,設(shè)置相應(yīng)標(biāo)識)static voidngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext){    char            *action;    ngx_int_t        ignore;    ngx_err_t        err;    ngx_signal_t    *sig;
          ignore = 0;
          err = ngx_errno;
          for (sig = signals; sig->signo != 0; sig++) { if (sig->signo == signo) { break; } }
          ngx_time_sigsafe_update();
          action = ""; // 根據(jù)當(dāng)前進(jìn)程的類型,做不一樣的處理邏輯 switch (ngx_process) { // master進(jìn)程處理 case NGX_PROCESS_MASTER: case NGX_PROCESS_SINGLE: // 根據(jù)控制標(biāo)識,設(shè)置相應(yīng)變量 // 該變量將被主循環(huán)服務(wù)讀取到 switch (signo) { case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): ngx_quit = 1; action = ", shutting down"; break;
          case ngx_signal_value(NGX_TERMINATE_SIGNAL): case SIGINT: ngx_terminate = 1; action = ", exiting"; break;
          case ngx_signal_value(NGX_NOACCEPT_SIGNAL): if (ngx_daemonized) { ngx_noaccept = 1; action = ", stop accepting connections"; } break;
          case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): ngx_reconfigure = 1; action = ", reconfiguring"; break;
          case ngx_signal_value(NGX_REOPEN_SIGNAL): ngx_reopen = 1; action = ", reopening logs"; break;
          case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) {
          /* * Ignore the signal in the new binary if its parent is * not changed, i.e. the old binary's process is still * running. Or ignore the signal in the old binary's * process if the new binary's process is already running. */
          action = ", ignoring"; ignore = 1; break; }
          ngx_change_binary = 1; action = ", changing binary"; break;
          case SIGALRM: ngx_sigalrm = 1; break;
          case SIGIO: ngx_sigio = 1; break;
          case SIGCHLD: ngx_reap = 1; break; }
          break; // worker 收到控制請求 case NGX_PROCESS_WORKER: case NGX_PROCESS_HELPER: // 同樣設(shè)置相當(dāng)標(biāo)識變量,在主循環(huán)中進(jìn)行處理響應(yīng) switch (signo) { case ngx_signal_value(NGX_NOACCEPT_SIGNAL): if (!ngx_daemonized) { break; } ngx_debug_quit = 1; /* fall through */ case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): ngx_quit = 1; action = ", shutting down"; break;
          case ngx_signal_value(NGX_TERMINATE_SIGNAL): case SIGINT: ngx_terminate = 1; action = ", exiting"; break;
          case ngx_signal_value(NGX_REOPEN_SIGNAL): ngx_reopen = 1; action = ", reopening logs"; break;
          case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): case SIGIO: action = ", ignoring"; break; }
          break; }
          if (siginfo && siginfo->si_pid) { ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "signal %d (%s) received from %P%s", signo, sig->signame, siginfo->si_pid, action);
          } else { ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "signal %d (%s) received%s", signo, sig->signame, action); }
          if (ignore) { ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0, "the changing binary signal is ignored: " "you should shutdown or terminate " "before either old or new binary's process"); } // 等待子進(jìn)程完成 if (signo == SIGCHLD) { ngx_process_get_status(); }
          ngx_set_errno(err);}
          static voidngx_process_get_status(void){ int status; char *process; ngx_pid_t pid; ngx_err_t err; ngx_int_t i; ngx_uint_t one;
          one = 0;
          for ( ;; ) { pid = waitpid(-1, &status, WNOHANG);
          if (pid == 0) { return; }
          if (pid == -1) { err = ngx_errno;
          if (err == NGX_EINTR) { continue; }
          if (err == NGX_ECHILD && one) { return; }
          /* * Solaris always calls the signal handler for each exited process * despite waitpid() may be already called for this process. * * When several processes exit at the same time FreeBSD may * erroneously call the signal handler for exited process * despite waitpid() may be already called for this process. */
          if (err == NGX_ECHILD) { ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, err, "waitpid() failed"); return; }
          ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, "waitpid() failed"); return; }

          one = 1; process = "unknown process";
          for (i = 0; i < ngx_last_process; i++) { if (ngx_processes[i].pid == pid) { ngx_processes[i].status = status; ngx_processes[i].exited = 1; process = ngx_processes[i].name; break; } }
          if (WTERMSIG(status)) {#ifdef WCOREDUMP ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%s %P exited on signal %d%s", process, pid, WTERMSIG(status), WCOREDUMP(status) ? " (core dumped)" : "");#else ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%s %P exited on signal %d", process, pid, WTERMSIG(status));#endif
          } else { ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "%s %P exited with code %d", process, pid, WEXITSTATUS(status)); }
          if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%s %P exited with fatal code %d " "and cannot be respawned", process, pid, WEXITSTATUS(status)); ngx_processes[i].respawn = 0; }
          ngx_unlock_mutexes(pid); }}


          ngx_signal_handler 主要處理了各標(biāo)識字段的設(shè)置,那么設(shè)置之后并沒有做更多的事,即沒有進(jìn)行exit()操作,它又是如何達(dá)到響應(yīng)控制的呢。實際上,當(dāng)進(jìn)程的標(biāo)識變量被設(shè)置之后,會被其主循環(huán)服務(wù)稍后處理。每一次處理任務(wù)時,都會去檢查相關(guān)標(biāo)識,比如如果標(biāo)識是退出,則主循環(huán)服務(wù)將結(jié)束自身的循環(huán)服務(wù),從而達(dá)到響應(yīng)退出命令的目的。

          實際上,我們在做操作命令時,只是讀取了一個nginx的pid即master進(jìn)程的pid, 所以控制實際上只向master發(fā)送了命令。只不過master接收到該命令后,會在必要的時候?qū)⑵鋫鬟_(dá)給到所有的worker。從而完成整體的控制。

          // master循環(huán)服務(wù)實現(xiàn)// os/unix/ngx_process_cycle.cvoidngx_master_process_cycle(ngx_cycle_t *cycle){    char              *title;    u_char            *p;    size_t             size;    ngx_int_t          i;    ngx_uint_t         sigio;    sigset_t           set;    struct itimerval   itv;    ngx_uint_t         live;    ngx_msec_t         delay;    ngx_core_conf_t   *ccf;
          sigemptyset(&set); sigaddset(&set, SIGCHLD); sigaddset(&set, SIGALRM); sigaddset(&set, SIGIO); sigaddset(&set, SIGINT); sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
          if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigprocmask() failed"); }
          sigemptyset(&set);

          size = sizeof(master_process);
          for (i = 0; i < ngx_argc; i++) { size += ngx_strlen(ngx_argv[i]) + 1; }
          title = ngx_pnalloc(cycle->pool, size); if (title == NULL) { /* fatal */ exit(2); }
          p = ngx_cpymem(title, master_process, sizeof(master_process) - 1); for (i = 0; i < ngx_argc; i++) { *p++ = ' '; p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size); }
          ngx_setproctitle(title);

          ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
          ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0);
          ngx_new_binary = 0; delay = 0; sigio = 0; live = 1;
          for ( ;; ) { if (delay) { if (ngx_sigalrm) { sigio = 0; delay *= 2; ngx_sigalrm = 0; }
          ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "termination cycle: %M", delay);
          itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; itv.it_value.tv_sec = delay / 1000; itv.it_value.tv_usec = (delay % 1000 ) * 1000;
          if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setitimer() failed"); } }
          ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");
          sigsuspend(&set);
          ngx_time_update();
          ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "wake up, sigio %i", sigio);
          // 只管讀取相應(yīng)標(biāo)識即可 if (ngx_reap) { ngx_reap = 0; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children"); // 清理worker live = ngx_reap_children(cycle); }
          if (!live && (ngx_terminate || ngx_quit)) { // woker退出后,master再退出 ngx_master_process_exit(cycle); }
          if (ngx_terminate) { if (delay == 0) { delay = 50; }
          if (sigio) { sigio--; continue; }
          sigio = ccf->worker_processes + 2 /* cache processes */;
          if (delay > 1000) { ngx_signal_worker_processes(cycle, SIGKILL); } else { ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_TERMINATE_SIGNAL)); }
          continue; }
          if (ngx_quit) { ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); ngx_close_listening_sockets(cycle);
          continue; }
          if (ngx_reconfigure) { ngx_reconfigure = 0;
          if (ngx_new_binary) { ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); ngx_noaccepting = 0;
          continue; }
          ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
          cycle = ngx_init_cycle(cycle); if (cycle == NULL) { cycle = (ngx_cycle_t *) ngx_cycle; continue; }
          ngx_cycle = cycle; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN); ngx_start_cache_manager_processes(cycle, 1);
          /* allow new processes to start */ ngx_msleep(100);
          live = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); }
          if (ngx_restart) { ngx_restart = 0; ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); live = 1; }
          if (ngx_reopen) { ngx_reopen = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); ngx_reopen_files(cycle, ccf->user); ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_REOPEN_SIGNAL)); }
          if (ngx_change_binary) { ngx_change_binary = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary"); ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv); }
          if (ngx_noaccept) { ngx_noaccept = 0; ngx_noaccepting = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); } }}

          master進(jìn)程的主要作用,實際也是管理worker,所以控制命令發(fā)送到master, 剩余工作就由master完成。總體來說,就是master先將命令發(fā)送給worker,然后自身最后再響應(yīng)命令,保證命令的正確執(zhí)行。

          woker進(jìn)程則主要負(fù)責(zé)真正的業(yè)務(wù)處理,以及接收master發(fā)達(dá)過來的控制命令。與master各有分工,其對應(yīng)控制指令只需自身響應(yīng)即可。

          // worker主循環(huán)的實現(xiàn)static voidngx_worker_process_cycle(ngx_cycle_t *cycle, void *data){    ngx_int_t worker = (intptr_t) data;
          ngx_process = NGX_PROCESS_WORKER; ngx_worker = worker;
          ngx_worker_process_init(cycle, worker);
          ngx_setproctitle("worker process");
          for ( ;; ) { // 只管讀取相應(yīng)標(biāo)識即可 if (ngx_exiting) { if (ngx_event_no_timers_left() == NGX_OK) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); ngx_worker_process_exit(cycle); } }
          ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle"); // 業(yè)務(wù)處理 ngx_process_events_and_timers(cycle);
          if (ngx_terminate) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); ngx_worker_process_exit(cycle); }
          if (ngx_quit) { ngx_quit = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "gracefully shutting down"); ngx_setproctitle("worker process is shutting down");
          if (!ngx_exiting) { ngx_exiting = 1; ngx_set_shutdown_timer(cycle); ngx_close_listening_sockets(cycle); ngx_close_idle_connections(cycle); } }
          if (ngx_reopen) { ngx_reopen = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); ngx_reopen_files(cycle, -1); } }}

          可以看到,worker的主體循環(huán)工作,大多是在響應(yīng)master的控制指定,也就是前面看到的接收到控制指令后,設(shè)置好相應(yīng)的標(biāo)識,在該主循環(huán)中進(jìn)行響應(yīng)。

            最后,我們來思考幾個問題:

              1. 如果我直接kill -9 掉master, 那么nginx將會如何?
              2. 如果直接kill -9 掉woker, 那么nginx又將如何?
              3. 如何優(yōu)雅的關(guān)閉nginx?
              4. 如果不使用nginx的控制命令,能否實現(xiàn)ngnix的優(yōu)雅關(guān)閉?(shell實現(xiàn))

          相信通過上面的理解,這些問題不在話下!




          往期精彩推薦



          騰訊、阿里、滴滴后臺面試題匯總總結(jié) — (含答案)

          面試:史上最全多線程面試題 !

          最新阿里內(nèi)推Java后端面試題

          JVM難學(xué)?那是因為你沒認(rèn)真看完這篇文章


          END


          關(guān)注作者微信公眾號 —《JAVA爛豬皮》


          了解更多java后端架構(gòu)知識以及最新面試寶典


          你點的每個好看,我都認(rèn)真當(dāng)成了


          看完本文記得給作者點贊+在看哦~~~大家的支持,是作者源源不斷出文的動力


          作者:等你歸去來

          出處:https://www.cnblogs.com/yougewe/p/14321413.html

          瀏覽 79
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品久久久中文字幕 | 国产A片大全 | 俺去操在线视频 | 爱情岛成人 18网站 | 日本黄在线观看 |