Tengine 如何查詢 server 塊
概述
本文的目標讀者是Tengine/Nginx 研發或者運維同學,如果自己對這塊邏輯非常清楚,那可以略過,如果在配置或者開發 Tengine/Nginx 過程中,有如下疑問的同學,本文或許能解答你多年的疑惑:
- 請求到達匹配的是哪個 server 塊?
- 為啥明明配置了 server 塊,還是沒有生效?
- 沒有這個域名的 server 塊,請求到底使用了哪個 server 塊?
- 要自己去匹配 server 塊的話,該從哪裡入手?
……
等等此類 server 塊有關的問題,在使用 Tengine 時可能經常有遇到,在配置的 server 塊較少時,比較容易識別出,但在 CDN 或者雲平臺接入層這種場景下,配置的 server 塊一般都非常多,少的有幾十上百個,多的成千上萬個都有可能,所以瞭解 Tengine 如何查詢 server 塊非常有利於日常問題排查。
配置
先來看看幾個配置:
server { listen 10.101.192.91:80 default_server; listen 80 default_server; listen 8080 default_server; server_name { return 200 "default-server: $server_name, host: $host"; } }server { listen 10.101.192.91:80; server_name { return 200 "80server: $server_name, host: $host"; } }server { listen 10.101.192.91:8080; server_name *.bb.com; default_type text/plain; location / { return 200 "8080server: $server_name, host: $host"; } }server { listen 10.101.192.91:8080; server_name { return 200 "8080server: $server_name, host: $host"; } }
上面配置了四個 server 塊,配置也非常簡單,第一個 server 塊配置了 default_server 引數,這個表明了這個是預設 server 塊的意思(準確地說是這個 listen 的 IP:Port 進來的請求預設 server 塊),監聽了兩個埠80和8080,匹配域名為
,第二個是監聽了 10.101.192.91:80 和匹配域名為
的 server 塊,第三個是監聽了 10.101.192.91:8080 和匹配泛域名
*.bb.com
的 server 塊,第四個是監聽了 10.101.192.91:8080 和匹配精確域名
的 server 塊。下面來驗證一下:
可以看出:
-
127.0.0.1:80 和 127.0.0.1:8080 都訪問到了第一個 server 塊
- 這是因為第一個 server 監聽了 :80 和 :8080 埠,其他 server 塊沒有監聽 127.0.0.1 相應的埠,127.0.0.1 的訪問只能匹配第一個 server 塊。
-
10.101.192.91:80 的訪問,域名和 server 塊匹配時使用了相應的 server 塊,不匹配時使用了第一個預設 server 塊
- IP:Port 匹配的情況下,再匹配到域名所在的 server 塊,域名跟 server_name 不匹配則匹配預設 server 塊。
-
10.101.192.91:8080 的訪問,域名先精確匹配到了
的 server 塊,然後再匹配到了泛域名 *.bb.com 的 server 塊,不匹配時使用了第三個隱式預設 server 塊
- 這裡涉及到泛域名和隱式預設 server 塊,泛域名的匹配是在精確域名之後,這個也比較好理解,隱式預設 server 塊是沒有在 listen 後面指定 default_server 引數的 server 塊, Tengine/Nginx 在解析配置時,每個 IP:Port 都有一個預設 server 塊,如果 listen 後面顯式指定了 default_server 引數則該 listen 所在的 server 就是這個 IP:Port 的預設 server 塊,如果沒有顯式指定 default_server 引數則該 IP:Port 的第一個 server 塊就是隱式預設 server 塊。
上面這些配置可以衍生出一些 debug 技巧:
if ($http_x_alicdn_debug_get_server = "on") { return 200 "$server_addr:$server_port, server_name: $server_name"; }
只要帶上請求頭
X-Alicdn-Debug-Get-Server: on
即可知道請求命中的是哪個 server 塊,這個配置對 server 塊非常多的系統 debug 非常有用,需要注意的是這個配置需要放到一個配置檔案和用 server_auto_include 載入,然後 tengine 會自動在所有 server 塊生效(nginx 沒有類似的配置命令)。
資料結構
我們再來看看 http 核心模組 server 塊的配置在資料結構上怎麼關聯的,其資料結構是:
typedef struct { /* array of the ngx_http_server_name_t, "server_name" directive */ ngx_array_t server_names; /* server ctx */ ngx_http_conf_ctx_t *ctx; u_char *file_name; ngx_uint_t line; ngx_str_t server_name;#if (T_NGX_SERVER_INFO) ngx_str_t server_admin;#endif size_t connection_pool_size; size_t request_pool_size; size_t client_header_buffer_size; ngx_bufs_t large_client_header_buffers; ngx_msec_t client_header_timeout; ngx_flag_t ignore_invalid_headers; ngx_flag_t merge_slashes; ngx_flag_t underscores_in_headers; unsigned listen:1;#if (NGX_PCRE) unsigned captures:1;#endif ngx_http_core_loc_conf_t **named_locations; } ngx_http_core_srv_conf_t;
這裡不細說這些欄位是幹嘛用的,主要看 ngx_http_core_srv_conf_t 怎麼與其他資料結構關聯,從上面的配置可以知道 server 是與 IP:Port 有關聯的,在 tengine/nginx 裡的關係如下:
typedef struct { ngx_http_listen_opt_t opt; ngx_hash_t hash; ngx_hash_wildcard_t *wc_head; ngx_hash_wildcard_t *wc_tail;#if (NGX_PCRE) ngx_uint_t nregex; ngx_http_server_name_t *regex;#endif /* the default server configuration for this address:port */ ngx_http_core_srv_conf_t *default_server; ngx_array_t servers; /* array of ngx_http_core_srv_conf_t */} ngx_http_conf_addr_t;
可以看出,IP:Port 的核心資料結構 ngx_http_conf_addr_t 裡面有預設 server 塊 default_server,以及該 IP:Port 關聯的所有 server 塊陣列 servers,其他幾個欄位不細展開了。tengine 把所有的 IP:Port 按 Port 拆分後將
ngx_http_conf_addr_t
放到了
ngx_http_conf_port_t
裡面了:
typedef struct { ngx_int_t family; in_port_t port; ngx_array_t addrs; /* array of ngx_http_conf_addr_t */} ngx_http_conf_port_t;
為什麼將 IP:Port 拆分呢,這是因為 listen 的 Port 如果沒有指定 IP,比如
listen 80;
,那 tengine/nginx 在建立監聽 socket 時的地址是 0.0.0.0 ,如果還有其他配置 listen 了精確 ip 和埠,比如
listen 10.101.192.91:80;
,那在核心是沒法建立這個 socket 的,第2節配置裡面的幾個 listen 在核心是這樣監聽的:
雖然 listen 了 80 和 10.101.192.91:80,但在核心都是 0.0.0.0:80,所以 tengine 需要用
ngx_http_conf_port_t
來記錄該埠的所有精確地址。但這個結構只是使用在配置階段,在監聽 socket 時轉換成了結構
ngx_http_port_t
和
ngx_http_in_addr_t
(這是因為 ip:port 和 server 塊是多對多的關係,需要重新組織和最佳化):
typedef struct { /* ngx_http_in_addr_t or ngx_http_in6_addr_t */ void *addrs; ngx_uint_t naddrs; } ngx_http_port_t;typedef struct { in_addr_t addr; ngx_http_addr_conf_t conf; } ngx_http_in_addr_t; typdef ngx_http_addr_conf_s ngx_http_addr_conf_t;struct ngx_http_addr_conf_s { /* the default server configuration for this address:port */ ngx_http_core_srv_conf_t *default_server; ngx_http_virtual_names_t *virtual_names; unsigned ssl:1; unsigned http2:1; unsigned proxy_protocol:1; };
其中,
ngx_http_port_t
記錄了該埠的所有精確地址和對應的 server 塊。而
ngx_http_port_t
放到了監聽的 socket 核心結構
ngx_listening_t
中:
typedef struct ngx_listening_s ngx_listening_t;struct ngx_listening_s { ngx_socket_t fd; struct sockaddr *sockaddr; socklen_t socklen; /* size of sockaddr */ size_t addr_text_max_len; ngx_str_t addr_text; // 省略…… /* handler of accepted connection */ ngx_connection_handler_pt handler; void *servers; /* array of ngx_http_in_addr_t, for example */ // 省略……};struct ngx_connection_s { // 省略…… ngx_listening_t *listening; // 省略……};
所以一個連線可以從 c->listening->servers 來查詢匹配的 server 塊。
tengine 中 ip:port 和 server 的大體關聯關係如下:
(可以透過這個圖來理解一下 tengine 如何查詢 server 塊)
從請求到 server 塊
上面講了 ip:port 和 server 的一些關係和核心資料結構,這一節來講講 tengine 從處理請求到匹配 server 的邏輯。
ngx_http_init_connection
是初始化連線的函式,在這個函式里面我們看到有這樣的邏輯:
void ngx_http_init_connection(ngx_connection_t *c) { // 省略…… ngx_http_port_t *port; ngx_http_in_addr_t *addr; ngx_http_connection_t *hc; // 省略…… /* find the server configuration for the address:port */ port = c->listening->servers; if (port->naddrs > 1) { // 省略…… sin = (struct sockaddr_in *) c->local_sockaddr; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } hc->addr_conf = &addr[i].conf; // 省略…… } else { // 省略…… addr = port->addrs; hc->addr_conf = &addr[0].conf; // 省略…… } /* the default server configuration for the address:port */ hc->conf_ctx = hc->addr_conf->default_server->ctx; // 省略……}
可以看出,初始化時,拿到了 socket 的 ip:port 後去匹配了最合適的配置,存到了 hc->addr_conf 指標中,這個就是上面講到的資料結構
ngx_http_addr_conf_t
指標,這裡面存了該 ip:port 關聯的所有 server 塊核心配置,在之後收到 HTTP 請求頭處理請求行或者處理 Host 頭時,再根據域名去 hc->addr_conf 裡面匹配出真實的 server 塊:
static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host) { // 省略…… ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; // 省略…… hc = r->http_connection; // 省略…… rc = ngx_http_find_virtual_server(r->connection, hc->addr_conf->virtual_names, host, r, &cscf); //建立 r 時,r->srv_conf 和 r->loc_conf 是 hc->conf_ctx 的預設配置 //查不到匹配的 server 塊則不需要設定 r->srv_conf 和 r->loc_conf if (rc == NGX_DECLINED) { return NGX_OK; } // 查到匹配的 server,使用真實 server 塊的配置 r->srv_conf = cscf->ctx->srv_conf; r->loc_conf = cscf->ctx->loc_conf; // 省略……}
函式
ngx_http_find_virtual_server
是查詢域名對應的 server 塊介面(這個函式還有另一個地方呼叫是在處理 SSL 握手遇到 SNI 時,這是因為在握手時也需要找到匹配的 server 塊裡面配置的證照)。
至此,server 塊配置的查詢邏輯結束,後續其他模組處理時可以從 r->srv_conf 和 r->loc_conf 查到自己模組的 server/location 塊配置了。
️本文作者:金九
本文為雲棲社群原創內容,未經允許不得轉載。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69947441/viewspace-2656619/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 如何查詢損壞塊
- 我如何調優SQL Server查詢SQLServer
- tengine 淘寶開源的web serverWebServer
- SQL Server如何查詢鎖已經kill lockSQLServer
- 如何找出你效能最差的SQL Server查詢SQLServer
- SQL Server 查詢分解SQLServer
- SQL Server遞迴查詢SQLServer遞迴
- SQL SERVER 查詢鎖資訊SQLServer
- SQL server跨庫查詢SQLServer
- Sql Server系列:子查詢SQLServer
- SQL server 查詢語句SQLServer
- SQL Server 分散式查詢SQLServer分散式
- SQL SERVER 動態查詢SQLServer
- SQL Server 跨域查詢SQLServer跨域
- 獲取查詢塊名字
- sql-server高階查詢SQLServer
- SQL Server阻塞查詢語句SQLServer
- Sql Server系列:巢狀查詢SQLServer巢狀
- SQL Server 查詢優化功能SQLServer優化
- sql server分組查詢示例SQLServer
- 關於查詢塊query blockBloC
- 一條SQL語句查詢塊分解及查詢轉換SQL
- SQL Server 跨資料庫查詢SQLServer資料庫
- Sql Server 的引數化查詢SQLServer
- sql-server不相關子查詢SQLServer
- sql-server相關子查詢SQLServer
- sql server的許可權查詢SQLServer
- Sql Server系列:多表連線查詢SQLServer
- SQL SERVER 跨伺服器查詢SQLServer伺服器
- 查詢當前SQL Server的版本SQLServer
- MS SQL Server查詢優化方法SQLServer優化
- SQL Server之查詢檢索操作SQLServer
- MS SQL Server的遞迴查詢SQLServer遞迴
- MS SQL Server查詢優化方法SQLServer優化
- 如何在tengine/nginx層做ABtestNginx
- dbForge Studio for SQL Server入門教程:如何建立和編輯查詢SQLServer
- SQL Server-簡單查詢語句SQLServer
- SQL Server查詢慢的解決方案SQLServer