<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ### 配置解析接口? ### [](http://tengine.taobao.org/book/chapter_11.html#id3 "永久鏈接至標題") ngx_init_cycle提供的是配置解析接口。接口是一個切入點,通過少量代碼提供一個完整功能的調用。配置解析接口分為兩個階段,一個是準備階段,另一個就是真正開始調用配置解析。準備階段指什么呢?主要是準備三點: 1. 準備內存 nginx根據以往的經驗(old_cycle)預測這一次的配置需要分配多少內存。比如,我們可以看這段: if (old_cycle->shared_memory.part.nelts) { n = old_cycle->shared_memory.part.nelts; for (part = old_cycle->shared_memory.part.next; part; part = part->next) { n += part->nelts; } } else { n = 1; } if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t)) != NGX_OK) { ngx_destroy_pool(pool); return NULL; } 這段代碼的意思是遍歷old_cycle,統計上一次系統中分配了多少塊共享內存,接著就按這個數據初始化當前cycle中共享內存的規模。 1. 準備錯誤日志 nginx啟動可能出錯,出錯就要記錄到錯誤日志中。而錯誤日志本身也是配置的一部分,所以不解析完配置,nginx就不能了解錯誤日志的信息。nginx通過使用上一個周期的錯誤日志來記錄解析配置時發生的錯誤,而在配置解析完成以后,nginx就用新的錯誤日志替換舊的錯誤日志。具體代碼摘抄如下,以說明nginx解析配置時使用old_cycle的錯誤日志: log = old_cycle->log; pool->log = log; cycle->log = log; 1. 準備數據結構 主要是兩個數據結果,一個是ngx_cycle_t結構,一個是ngx_conf_t結構。前者用于存放所有CORE模塊的配置,后者則是用于存放解析配置的上下文信息。具體代碼如下: for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_CORE_MODULE) { continue; } module = ngx_modules[i]->ctx; if (module->create_conf) { rv = module->create_conf(cycle); if (rv == NULL) { ngx_destroy_pool(pool); return NULL; } cycle->conf_ctx[ngx_modules[i]->index] = rv; } } conf.ctx = cycle->conf_ctx; conf.cycle = cycle; conf.pool = pool; conf.log = log; conf.module_type = NGX_CORE_MODULE; conf.cmd_type = NGX_MAIN_CONF; 準備好了這些內容,nginx開始調用配置解析模塊,其代碼如下: if (ngx_conf_param(&conf) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } 第一個if解析nginx命令行參數’-g’加入的配置。第二個if解析nginx配置文件。好的設計就體現在接口極度簡化,模塊之間的耦合非常低。這里只使用區區10行完成了配置的解析。在這里,我們先淺嘗輒止,具體nginx如何解析配置,我們將在后面的小節做細致的介紹。 [](https://github.com/taobao/nginx-book/issues/new?title=nginx%E7%9A%84%E5%90%AF%E5%8A%A8%E9%98%B6%E6%AE%B5%20(30%25)%C2%B6+%E9%85%8D%E7%BD%AE%E8%A7%A3%E6%9E%90%C2%B6&body=current%20content%3A%0A%20...%0Aadvice%3A%0A%20...%0Areason%3A%0A%20...%0A "點擊提交Issue,反饋你的意見...") ### 配置解析[](http://tengine.taobao.org/book/chapter_11.html#id4 "永久鏈接至標題") [](http:// "點擊提交Issue,反饋你的意見...") ### 通用過程[](http://tengine.taobao.org/book/chapter_11.html#id5 "永久鏈接至標題") 配置解析模塊在ngx_conf_file.c中實現。模塊提供的接口函數主要是ngx_conf_parse,另外,模塊提供一個單獨的接口ngx_conf_param,用來解析命令行傳遞的配置,當然,這個接口也是對ngx_conf_parse的包裝。 ngx_conf_parse函數支持三種不同的解析環境: 1. parse_file:解析配置文件; 1. parse_block:解析塊配置。塊配置一定是由“{”和“}”包裹起來的; 1. parse_param:解析命令行配置。命令行配置中不支持塊指令。 我們先來鳥瞰nginx解析配置的流程,整個過程可參見下面示意圖: 圖11-2 這是一個遞歸的過程。nginx首先解析core模塊的配置。core模塊提供一些塊指令,這些指令引入其他類型的模塊,nginx遇到這些指令,就重新迭代解析過程,解析其他模塊的配置。這些模塊配置中又有一些塊指令引入新的模塊類型或者指令類型,nginx就會再次迭代,解析這些新的配置類型。比如上圖,nginx遇到“events”指令,就重新調用ngx_conf_parse()解析event模塊配置,解析完以后ngx_conf_parse()返回,nginx繼續解析core模塊指令,直到遇到“http”指令。nginx再次調用ngx_conf_parse()解析http模塊配置的http級指令,當遇到“server”指令時,nginx又一次調用ngx_conf_parse()解析http模塊配置的server級指令。 了解了nginx解析配置的流程,我們來看其中的關鍵函數ngx_conf_parse()。 ngx_conf_parse()解析配置分成兩個主要階段,一個是詞法分析,一個是指令解析。 詞法分析通過ngx_conf_read_token()函數完成。指令解析有兩種方式,其一是使用nginx內建的指令解析機制,其二是使用第三方自定義指令解析機制。自定義指令解析可以參見下面的代碼: if (cf->handler) { rv = (*cf->handler)(cf, NULL, cf->handler_conf); if (rv == NGX_CONF_OK) { continue; } if (rv == NGX_CONF_ERROR) { goto failed; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv); goto failed; } 這里注意cf->handler和cf->handler_conf兩個屬性,其中handler是自定義解析函數指針,handler_conf是conf指針。 下面著重介紹nginx內建的指令解析機制。本機制分為4個步驟: 1. 只有處理的模塊的類型是NGX_CONF_MODULE或者是當前正在處理的模塊類型,才可能被執行。nginx中有一種模塊類型是NGX_CONF_MODULE,當前只有ngx_conf_module一種,只支持一條指令“include”。“include”指令的實現我們后面再進行介紹。 ngx_modules[i]->type != NGX_CONF_MODULE && ngx_modules[i]->type != cf->module_type 1. 匹配指令名,判斷指令用法是否正確。 1. 指令的Context必須當前解析Context相符; !(cmd->type & cf->cmd_type) 1. 非塊指令必須以“;”結尾; !(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK 1. 塊指令必須后接“{”; (cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START 1. 指令參數個數必須正確。注意指令參數有最大值NGX_CONF_MAX_ARGS,目前值為8。 if (!(cmd->type & NGX_CONF_ANY)) { if (cmd->type & NGX_CONF_FLAG) { if (cf->args->nelts != 2) { goto invalid; } } else if (cmd->type & NGX_CONF_1MORE) { if (cf->args->nelts < 2) { goto invalid; } } else if (cmd->type & NGX_CONF_2MORE) { if (cf->args->nelts < 3) { goto invalid; } } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) { goto invalid; } else if (!(cmd->type & argument_number[cf->args->nelts - 1])) { goto invalid; } } 1. 取得指令工作的conf指針。 if (cmd->type & NGX_DIRECT_CONF) { conf = ((void **) cf->ctx)[ngx_modules[i]->index]; } else if (cmd->type & NGX_MAIN_CONF) { conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); } else if (cf->ctx) { confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[ngx_modules[i]->ctx_index]; } } 1. NGX_DIRECT_CONF常量單純用來指定配置存儲區的尋址方法,只用于core模塊。 1. NGX_MAIN_CONF常量有兩重含義,其一是指定指令的使用上下文是main(其實還是指core模塊),其二是指定配置存儲區的尋址方法。所以,在代碼中常常可以見到使用上下文是main的指令的cmd->type屬性定義如下: NGX_MAIN_CONF|NGX_DIRECT_CONF|... 表示指令使用上下文是main,conf尋址方式是直接尋址。 使用NGX_MAIN_CONF還表示指定配置存儲區的尋址方法的指令有4個:“events”、“http”、“mail”、“imap”。這四個指令也有共同之處——都是使用上下文是main的塊指令,并且塊中的指令都使用其他類型的模塊(分別是event模塊、http模塊、mail模塊和mail模塊)來處理。 NGX_MAIN_CONF|NGX_CONF_BLOCK|... 后面分析ngx_http_block()函數時,再具體分析為什么需要NGX_MAIN_CONF這種配置尋址方式。 1. 除開core模塊,其他類型的模塊都會使用第三種配置尋址方式,也就是根據cmd->conf的值從cf->ctx中取出對應的配置。舉http模塊為例,cf->conf的可選值是NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET,分別對應“http{}”、“server{}”、“location{}”這三個http配置級別。 1. 執行指令解析回調函數 rv = cmd->set(cf, cmd, conf); cmd是詞法分析得到的結果,conf是上一步得到的配置存貯區地址。 [](http:// "點擊提交Issue,反饋你的意見...") ### http的解析[](http://tengine.taobao.org/book/chapter_11.html#http "永久鏈接至標題") http是作為一個core模塊被nginx通用解析過程解析的,其核心就是“http”塊指令回調,它完成了http解析的整個功能,從初始化到計算配置結果。 因為這是本書第一次提到塊指令,所以在這里對其做基本介紹。 塊指令的流程是: 1. 創建并初始化上下文環境; 1. 調用通用解析流程解析; 1. 根據解析結果進行后續合并處理; 1. 善后工作。 下面我們以“http”指令為例來介紹這個流程: [](http:// "點擊提交Issue,反饋你的意見...") #### 創建并初始化上下文環境[](http://tengine.taobao.org/book/chapter_11.html#id6 "永久鏈接至標題") ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); *(ngx_http_conf_ctx_t **) conf = ctx; ... ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; mi = ngx_modules[m]->ctx_index; if (module->create_main_conf) { ctx->main_conf[mi] = module->create_main_conf(cf); } if (module->create_srv_conf) { ctx->srv_conf[mi] = module->create_srv_conf(cf); } if (module->create_loc_conf) { ctx->loc_conf[mi] = module->create_loc_conf(cf); } } pcf = *cf; cf->ctx = ctx; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; if (module->preconfiguration) { if (module->preconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } } http模塊的上下文環境ctx(注意我們在通用解析流程中提到的ctx是同一個東西)非常復雜,它是由三個指針數組組成的:main_conf、srv_conf、loc_conf。根據上面的代碼可以看到,這三個數組的元素個數等于系統中http模塊的個數。想想我們平時三四十個http模塊的規模,大家也應該可以理解這一塊結構的龐大。nginx還為每個模塊分別執行對應的create函數分配空間。我們需要注意后面的這一句“cf->ctx = ctx;”,正是這一句將解析配置的上下文切換成剛剛建立的ctx。最后一段代碼通過調用各個http模塊的preconfiguration回調函數完成了對應模塊的預處理操作,其主要工作是創建模塊用到的變量。 [](http:// "點擊提交Issue,反饋你的意見...") #### 調用通用解析流程解析[](http://tengine.taobao.org/book/chapter_11.html#id7 "永久鏈接至標題") cf->module_type = NGX_HTTP_MODULE; cf->cmd_type = NGX_HTTP_MAIN_CONF; rv = ngx_conf_parse(cf, NULL); 基本上所有的塊指令都類似上面的三行語句(例外是map,它用的是cf->handler),改變通用解析流程的工作狀態,然后調用通用解析流程。 [](http:// "點擊提交Issue,反饋你的意見...") #### 根據解析結果進行后續合并處理[](http://tengine.taobao.org/book/chapter_11.html#id8 "永久鏈接至標題") for (m = 0; ngx_modules[m]; m++) { if (module->init_main_conf) { rv = module->init_main_conf(cf, ctx->main_conf[mi]); } rv = ngx_http_merge_servers(cf, cmcf, module, mi); } for (s = 0; s < cmcf->servers.nelts; s++) { if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) { return NGX_CONF_ERROR; } } if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } for (m = 0; ngx_modules[m]; m++) { if (module->postconfiguration) { if (module->postconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } } if (ngx_http_variables_init_vars(cf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) { return NGX_CONF_ERROR; } 以上是http配置處理最重要的步驟。首先,在這里調用了各個模塊的postconfiguration回調函數完成了模塊配置過程。更重要的是,它為nginx建立了一棵完整的配置樹(葉子節點為location,包含location的完整配置)、完整的location搜索樹、一張變量表、一張完成的階段處理回調表(phase handler)、一張server對照表和一張端口監聽表。下面我們將分別介紹這些配置表的生成過程。 ##### location配置樹[](http://tengine.taobao.org/book/chapter_11.html#location "永久鏈接至標題") 介紹這部分以前,先說明一個nginx的公理 公理11-1:所有存放參數為NGX_HTTP_SRV_CONF_OFFSET的配置,配置僅在請求匹配的虛擬主機(server)上下文中生效,而所有存放參數為NGX_HTTP_LOC_CONF_OFFSET的配置,配置僅在請求匹配的路徑(location)上下文中生效。 正因為有公理11-1,所以nginx需要調用merge_XXX回調函數合并配置。具體的原因是很多配置指令可以放在不同配置層級,比如access_log既可以在http塊中配置,又可以在server塊中配置,還可以在location塊中配置。 但是因為公理11-1,access_log指令配置只有在路徑(location)上下文中生效,所以需要將在http塊中配置的access_log指令的配置向路徑上下文做兩次傳遞,第一次從HTTP(http)上下文到虛擬主機(server)上下文,第二次從虛擬主機上下文到路徑上下文。 可能有人會疑惑,為什么需要傳遞和合并呢?難道它們不在一張表里么?對,在創建并初始化上下文環境的過程中,大家已經看到,nginx為HTTP上下文創建了main_conf,為虛擬主機上下文創建了srv_conf,為路徑上下文創建了loc_conf。但是,這張表只是用于解析在http塊但不包含server塊中定義的指令。而后面我們會看到,在server塊指令中,同樣建立了srv_conf和loc_conf,用于解析在server塊但不含location塊中定義的指令。所以nginx其實維護了很多張配置表,因此nginx必須將配置在這些表中從頂至下不斷傳遞。 前面列出的 for (m = 0; ngx_modules[m]; m++) { if (module->init_main_conf) { rv = module->init_main_conf(cf, ctx->main_conf[mi]); } rv = ngx_http_merge_servers(cf, cmcf, module, mi); } 就是初始化HTTP上下文,并且完成兩步配置合并操作:從HTTP上下文合并到虛擬主機上下文,以及從虛擬主機上下文合并到路徑上下文。其中,合并到路徑上下問的操作是在ngx_http_merge_servers函數中進行的,見 if (module->merge_loc_conf) { /* merge the server{}'s loc_conf */ /* merge the locations{}' loc_conf's */ } 大家注意觀察ngx_http_merge_servers函數中的這段,先將HTTP上下文中的location配置合并到虛擬主機上下文,再將虛擬主機上下文中的location配置合并到路徑上下文。 ##### location搜索樹[](http://tengine.taobao.org/book/chapter_11.html#id9 "永久鏈接至標題") 公理11-2:nginx搜索路徑時,正則匹配路徑和其他的路徑分開搜。 公理11-3:nginx路徑可以嵌套。 所以,nginx存放location的有兩個指針,分別是 struct ngx_http_core_loc_conf_s { ... ngx_http_location_tree_node_t *static_locations; #if (NGX_PCRE) ngx_http_core_loc_conf_t **regex_locations; #endif ... } 通過這段代碼,大家還可以發現一點——nginx的正則表達式需要PCRE支持。 正則表達式的路徑是個指針數組,指針類型就是ngx_http_core_loc_conf_t,所以數據結構決定算法,正則表達式路徑的添加非常簡單,就是在表中插入一項,這里不做介紹。 而其他路徑,保存在ngx_http_location_tree_node_t指針指向的搜索樹static_locations,則是變態復雜,可以看得各位大汗淋漓。 為了說明這棵樹的構建,我們先了解其他路徑包含哪些: 1. 普通前端匹配的路徑,例如location / {} 1. 搶占式前綴匹配的路徑,例如location ^~ / {} 1. 精確匹配的路徑,例如location = / {} 1. 命名路徑,比如location @a {} 1. 無名路徑,比如if {}或者limit_except {}生成的路徑 我們再來看ngx_http_core_loc_conf_t中如何體現這些路徑: | 普通前端匹配的路徑 | 無 | |-----|-----| | 搶占式前綴匹配的路徑 | noregex = 1 | | 精確匹配的路徑 | exact_match = 1 | | 命名路徑 | named = 1 | | 無名路徑 | noname = 1 | | 正則路徑 | regex != NULL | 有了這些基礎知識,可以看代碼了。首先是ngx_http_init_locations函數 ngx_queue_sort(locations, ngx_http_cmp_locations); for (q = ngx_queue_head(locations); q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { clcf = lq->exact ? lq->exact : lq->inclusive; if (ngx_http_init_locations(cf, NULL, clcf) != NGX_OK) { return NGX_ERROR; } if (clcf->regex) { r++; if (regex == NULL) { regex = q; } continue; } if (clcf->named) { n++; if (named == NULL) { named = q; } continue; } if (clcf->noname) { break; } } if (q != ngx_queue_sentinel(locations)) { ngx_queue_split(locations, q, &tail); } if (named) { ... cscf->named_locations = clcfp; ... } if (regex) { ... pclcf->regex_locations = clcfp; ... } 大家可以看到,這個函數正是根據不同的路徑類型將locations分成多段,并以不同的指針引用。首先注意開始的排序,根據ngx_http_cmp_locations比較各個location,排序以后的順序依次是 1. 精確匹配的路徑和兩類前綴匹配的路徑(字母序,如果某個精確匹配的路徑的名字和前綴匹配的路徑相同,精確匹配的路徑排在前面) 1. 正則路徑(出現序) 1. 命名路徑(字母序) 1. 無名路徑(出現序) 這樣nginx可以簡單的截斷列表得到不同類型的路徑,nginx也正是這樣處理的。 另外還要注意一點,就是ngx_http_init_locations的迭代調用,這里的clcf引用了兩個我們沒有介紹過的字段exact和inclusive。這兩個字段最初是在ngx_http_add_location函數(添加location配置時必然調用)中設置的: 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; } 當然這部分的具體邏輯我們在介紹location解析是再具體說明。 接著我們看ngx_http_init_static_location_trees函數。通過剛才的ngx_http_init_locations函數,留在locations數組里面的還有哪些類型的路徑呢? 還有普通前端匹配的路徑、搶占式前綴匹配的路徑和精確匹配的路徑這三類。 if (ngx_http_join_exact_locations(cf, locations) != NGX_OK) { return NGX_ERROR; } ngx_http_create_locations_list(locations, ngx_queue_head(locations)); pclcf->static_locations = ngx_http_create_locations_tree(cf, locations, 0); if (pclcf->static_locations == NULL) { return NGX_ERROR; } 請注意除開這段核心代碼,這個函數也有一個自迭代過程。 ngx_http_join_exact_locations函數是將名字相同的精確匹配的路徑和兩類前綴匹配的路徑合并,合并方法 lq->inclusive = lx->inclusive; ngx_queue_remove(x); 簡言之,就是將前綴匹配的路徑放入精確匹配的路徑的inclusive指針中,然后從列表刪除前綴匹配的路徑。 ngx_http_create_locations_list函數將和某個路徑名擁有相同名稱前綴的路徑添加到此路徑節點的list指針域下,并將這些路徑從locations中摘除。其核心代碼是 ngx_queue_split(&lq->list, x, &tail); ngx_queue_add(locations, &tail); ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list)); ngx_http_create_locations_list(locations, x); ngx_http_create_locations_tree函數則將剛才劃分的各個list繼續細分,形成一個二分搜索樹,每個中間節點代表一個location,每個location有如下字段: 1. exact:兩類前綴匹配路徑的inclusive指針域指向這兩類路徑的配置上下文; 1. inclusive:精確匹配路徑的exact指針域指向這些路徑的配置上下文; 1. auto_redirect:為各種upstream模塊,比如proxy、fastcgi等等開啟自動URI填充的功能; 1. len:路徑前綴的長度。任何相同前綴的路徑的len等于該路徑名長度減去公共前綴的長度。比如路徑/a和/ab,前者的len為2,后者的len也為1; 1. name:路徑前綴,任何相同前綴的路徑的name是其已于公共前綴的部分。仍舉路徑/a和/ab為例,前者的name為/a,后者的name為b; 1. left:左子樹,當然是長度短或者字母序小的不同前綴的路徑; 1. right:右子樹,當然是長度長或者字母序大的不同前綴的路徑。 通過上面三個步驟,nginx就將locations列表中各種類型的路徑分類處理并由不同的指針引用。對于前綴路徑和精確匹配的路徑,形成一棵獨特的二分前綴樹。 ##### 變量表[](http://tengine.taobao.org/book/chapter_11.html#id10 "永久鏈接至標題") 變量表的處理相對簡單,即對照變量名表,為變量表中的每一個元素設置對應的get_handler和data字段。在前面的章節大家已經知道,變量表variables用以處理索引變量,而變量名表variables_keys用于處理可按變量名查找的變量。對于通過ngx_http_get_variable_index函數創建的索引變量,在變量表variables中的get_handler初始為空,如果沒有認為設置的話,將會在這里進行初始化。 特殊變量的get_handler初始化也在這里進行: | 變量前綴 | get_handler | 標志 | |-----|-----|-----| | http | ngx_http_variable_unknown_header_in | ? | | sent_http | ngx_http_variable_unknown_header_out | ? | | upstream_http | ngx_http_upstream_header_variable | NGX_HTTP_VAR_NOCACHEABLE | | cookie | ngx_http_variable_cookie | ? | | arg | ngx_http_variable_argument | NGX_HTTP_VAR_NOCACHEABLE | ##### 階段處理回調表[](http://tengine.taobao.org/book/chapter_11.html#id11 "永久鏈接至標題") 按照下表順序將各個模塊設置的phase handler依次加入cmcf->phase_engine.handlers列表,各個phase的phase handler的checker不同。checker主要用于限定某個phase的框架邏輯,包括處理返回值。 <table border="1" class="docutils" style="margin: 0px -0.5em; border: 0px;"><colgroup><col width="37%"/><col width="40%"/><col width="23%"/></colgroup><tbody valign="top"><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">處理階段PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">checker</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">可自定義handler</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_POST_READ_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_generic_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">是</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_SERVER_REWRITE_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_rewrite_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">是</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_FIND_CONFIG_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_find_config_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">否</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_REWRITE_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_rewrite_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">是</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_POST_REWRITE_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_post_rewrite_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">否</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_PREACCESS_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_generic_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">是</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_ACCESS_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_access_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">是</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_POST_ACCESS_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_post_access_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">否</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_TRY_FILES_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_try_files_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">否</td></tr><tr class="row-even"/><tr class="row-odd"><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">NGX_HTTP_CONTENT_PHASE</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">ngx_http_core_content_phase</td><td rowspan="2" style="padding: 1px 8px 1px 5px; border-top-width: 0px; border-right-width: 0px; border-left-width: 0px; border-bottom-color: rgb(170, 170, 170);">是</td></tr><tr class="row-even"/></tbody></table> 注意相同PHASE的phase handler是按模塊順序的反序加入回調表的。另外在NGX_HTTP_POST_REWRITE_PHASE中,ph->next指向NGX_HTTP_FIND_CONFIG_PHASE第一個phase handler,以實現rewrite last邏輯。 ##### server對照表[](http://tengine.taobao.org/book/chapter_11.html#server "永久鏈接至標題") 大家如果讀過nginx的“Server names”這篇官方文檔,會了解nginx對于server name的處理分為4中情況:精確匹配、前綴通配符匹配、后綴通配符匹配和正則匹配。那么,下面是又一個公理, 公理11-4:nginx對于不同類型的server name分別處理。 所以,所謂server對照表,其實是四張表,分別對應四種類型的server。數據結構決定算法,四張表決定了nginx必須建立這四張表的行為。鑒于前三種類型和正則匹配可以分成兩大類,nginx使用兩套策略生成server對照表——對正則匹配的虛擬主機名,nginx為其建立一個數組,按照主機名在配置文件的出現順序依次寫入數組;而對于其他虛擬主機名,nginx根據它們的類型為它們分別存放在三張hash表中。三張hash表的結構完全相同,但對于前綴通配或者后綴通配這兩種類型的主機名,nginx對通配符進行的預處理不同。其中“.taobao.com”這種特殊的前綴通配與普通的前綴通配處理又有不同。我們現在來介紹這些不同。 處理前綴通配是將字符串按節翻轉,然后去掉通配符。舉個例子,“*.example.com”會被轉換成“com.example.\0”,而特殊的前綴通配“.example.com”會被轉換成“com.example\0”。 處理后綴通配更簡單,直接去掉通配符。也舉個例子,“www.example.*”會被轉換成“www.example\0”。 ##### 端口監聽表[](http://tengine.taobao.org/book/chapter_11.html#id12 "永久鏈接至標題") 對于所有寫在server配置中的listen指令,nginx開始會建立一張server和端口的對照索引表。雖然這不是本節的要點,但要說明索引表到監聽表的轉換過程,還是需要描述其結構。如圖11-3所示,這張索引表是二級索引,第一級索引以listen指定的端口為鍵,第二級索引以listen指定的地址為鍵,索引的對象就是server上下文數據結構。而端口監視表是兩張表,其結構如圖11-4所示。 索引表和監聽表在結構上非常類似,但是卻有一個非常明顯的不同。索引表中第一張表的各表項的端口是唯一的,而監聽表的第一張表中的不同表項的端口卻可能是相同的。之所以出現這樣的差別,是因為nginx會為監聽表第一張表中的每一項分別建立監聽套接字,而在索引表中,如果配置顯式定義了需要監聽不同IP地址的相同端口,它在索引表中會放在同一個端口的二級索引中,而在監聽表中必須存放為
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看