靈活應(yīng)用Nginx Map:實(shí)戰(zhàn)經(jīng)驗(yàn)與實(shí)用方法
Map 介紹
map 指令是 Nginx 配置文件中的一個(gè)指令,它用于在請求處理期間創(chuàng)建變量,并根據(jù)指定的鍵值對映射關(guān)系進(jìn)行值的映射。它可以用于動(dòng)態(tài)地生成配置項(xiàng)的值,例如根據(jù)請求的 URL、請求頭、查詢參數(shù)等信息來生成不同的值。
map 指令的語法如下:
map?$variable?$new_variable?{
????key?value;
????key?value;
????...
????default?value;
}
其中,$variable 是要映射的變量,可以是任何有效的 Nginx 變量,如 $uri、$args、$http_host 等;$new_variable 是映射后的新變量名,可以自定義;key 是映射的鍵,可以是字符串、正則表達(dá)式或者變量;value 是映射的值,可以是字符串、變量或者表達(dá)式;default 是默認(rèn)值,當(dāng)沒有匹配到任何鍵時(shí)使用。
map 指令的作用是將 $variable 的值根據(jù)鍵值對映射關(guān)系映射到 $new_variable 上,并且這個(gè)映射是在配置文件加載時(shí)進(jìn)行的,不會(huì)在請求處理期間進(jìn)行計(jì)算。一旦映射關(guān)系確定,映射的值會(huì)保存在 $new_variable 中,并可以在配置文件中的其他地方使用。
map 指令可以用于許多場景,例如根據(jù)請求的路徑生成重寫規(guī)則、根據(jù)請求頭判斷是否啟用緩存、根據(jù)查詢參數(shù)配置不同的后端服務(wù)等。它為 Nginx 提供了更加靈活和動(dòng)態(tài)的配置選項(xiàng)。下面看幾個(gè)經(jīng)典使用場景。
巧用map實(shí)現(xiàn)Nginx stream基于源IP做路由負(fù)載
業(yè)務(wù)方新加了一個(gè)業(yè)務(wù)網(wǎng)關(guān),上線前需要做個(gè)驗(yàn)證,把來源ip為27.38.x.255和116.30.x.170訪問用戶路由到新new_gateway做驗(yàn)證,其他的繼續(xù)走old_gateway。
??
stream?{
????
???log_format??basic???'$time_iso8601?$remote_addr?'
????????????????????????'$protocol?$status?$bytes_sent?$bytes_received?'
????????????????????????'$session_time?$upstream_addr?'
????????????????????????'"$upstream_bytes_sent"?"$upstream_bytes_received"?"$upstream_connect_time"';
????access_log???/var/log/nginx/stream.log??basic?buffer=1k?flush=5s;?
????upstream?old_gateway?{
????????server?10.6.11.86:8080;
????}
????upstream?new_gateway?{
????????server?10.6.11.86:80;
????}
????map?$remote_addr?$backend_svr?{
????????"27.38.x.255"?"new_gateway";
????????"116.30.x.170"?"new_gateway";
????????default?"old_gateway";
????}
????
????server?{
????????listen?8080;
????????proxy_connect_timeout?2s;
????????#ssl_preread?on;
????????#proxy_protocol?on;
????????proxy_pass?$backend_svr;
????}
}
要基于Nginx變量($cookie_uin)限制請求數(shù)
現(xiàn)在有個(gè)uri(/v3/aggregate/broker/trade/NewIpoFinancing)要基于Nginx變量($cookie_uin)限制請求數(shù).
1 限制每個(gè)uin 2s一個(gè)請求,如果$cookie_uin 為空,則給一個(gè)默認(rèn)的uin
//在Nginx主配置文件添加如下配置
http?{
????include???????mime.types;
????...
????map?$cookie_uin?$my_cookie_uin?{
????????default?$cookie_uin;
????????'-'?10010;
????????''?10010;
????}
????limit_req_zone?$my_cookie_uin?zone=limit_per_uin:10m?rate=30r/m;
?
????...
?
?}
//?uri?接口配置文檔
location?~?^/v3/aggregate/broker/trade/NewIpoFinancing?{
????limit_req?zone=limit_per_uin?burst=3?nodelay;
????include?/etc/nginx/vhost/common/cors.conf;
????proxy_pass?http://access_trade3;
}
2 限制每個(gè)uin 2s一個(gè)請求,如果$cookie_uin 為空,返回403
//在Nginx主配置文件添加如下配置
http?{
????include???????mime.types;
????...
????map?$cookie_uin?$limit_key?{
????????default?0;
????????'-'?1;
????????''?1;
????}
????limit_req_zone?$cookie_uin?zone=limit_per_uin:10m?rate=30r/m;
?
????...
?
?}
//?uri?接口配置文檔
location?~?^/v3/aggregate/broker/trade/NewIpoFinancing?{
????if?($limit_key?=?1)?{
????????return?403;
????}
????limit_req?zone=limit_per_uin?burst=3?nodelay;
????include?/etc/nginx/vhost/common/cors.conf;
????proxy_pass?http://access_trade3;
}
3 限制每個(gè)uin 2s一個(gè)請求,如果$cookie_uin 為空,返回403, 如果是vip uin不做限制
//在Nginx主配置文件添加如下配置
http?{
????include???????mime.types;
????...
????map?$cookie_uin?$limit_key?{
????????default?0;
????????'-'?1;
????????''?1;
????????'666666'?10;
????????'666667'?10;
????????'666668'?10;
????}
????limit_req_zone?$cookie_uin?zone=limit_per_uin:10m?rate=30r/m;
????...
?
?}
//?uri?接口配置文檔
location?~?^/v3/aggregate/broker/trade/NewIpoFinancing?{
????if?($limit_key?=?1)?{
????????return?403;
????}
????#vip?uin
????error_page?410?=?@nolimit;
????if?($limit_key?=?10)?{
????????return?410;
????}
????limit_req?zone=limit_per_uin?burst=3?nodelay;
????include?/etc/nginx/vhost/common/cors.conf;
????proxy_pass?http://access_trade3;
}
location?@nolimit?{
????include?/etc/nginx/vhost/common/cors.conf;
????proxy_pass?http://access_trade3;
}
利用Nginx Map實(shí)現(xiàn)正向代理動(dòng)態(tài)切換
???map?$host?$idc?{
????????default?lg;
???}
???
????map?$idc?$backend_4430_svr?{
????????default???https://$host$request_uri;
????????lg????????https://$host$request_uri;
????????kx????????http://10.0.x.136:4430;
????}
????
????map?$idc?$backend_8880_svr?{
????????default???http://$host$request_uri;
????????lg????????http://$host$request_uri;
????????kx????????http://10.0.x.13:8880;
????}
????
????map?$idc?$backend_4480_svr?{
????????default???$http_PROT://$http_DN:$http_Port;
????????lg????????$http_PROT://$http_DN:$http_Port;
????????kx????????http://10.0.x.13:4480;
????}
????
????server?{
????????listen???????8880;
????????location?/?{
????????????resolver?127.0.0.1;
????????????proxy_pass??$backend_8880_svr;
????????????proxy_set_header?Host?$host;
????????????proxy_set_header?X-Real-IP??$remote_addr;
????????????proxy_set_header?X-Forwarded-For?$proxy_add_x_forwarded_for;
???????}
????}
???
????server?{
????????listen???????4480;
????????location?/?{
????????????resolver?127.0.0.1;
????????????proxy_pass??$backend_4480_svr;
????????????proxy_set_header?Host?$http_DN;
????????????proxy_connect_timeout?10s;
????????????proxy_read_timeout?20s;
???????}
????}
????server?{
????????listen???????4430;
????????location?/?{
????????????resolver?127.0.0.1;
????????????proxy_pass??$backend_4430_svr;
????????????proxy_set_header?Host?$host;
????????????proxy_set_header?X-Forwarded-For?$proxy_add_x_forwarded_for;
???????}
????}
根據(jù)請求頭中的PROT、DN和Port字段的值,將請求轉(zhuǎn)發(fā)到不同的后端服務(wù)器。其中,PROT字段表示請求的協(xié)議(http或https),DN字段表示請求的域名,Port字段表示請求的端口號。
根據(jù)配置文件中的map指令,將$host(請求頭中的域名)映射為$idc,然后根據(jù)$idc的值將請求轉(zhuǎn)發(fā)到相應(yīng)的后端服務(wù)器。
具體的轉(zhuǎn)發(fā)規(guī)則如下:
當(dāng)$idc的值為ns時(shí),將請求轉(zhuǎn)發(fā)到$backend_4430_svr,并將請求頭中的Host字段和X-Forwarded-For字段傳遞給后端服務(wù)器。
當(dāng)$idc的值為ft時(shí),將請求轉(zhuǎn)發(fā)到$backend_8880_svr,并將請求頭中的Host字段、X-Real-IP字段和X-Forwarded-For字段傳遞給后端服務(wù)器。
當(dāng)$idc的值為其他值時(shí),將請求轉(zhuǎn)發(fā)到$backend_4480_svr,并將請求頭中的Host字段、PROT字段、DN字段和Port字段傳遞給后端服務(wù)器。
請求的具體轉(zhuǎn)發(fā)地址是根據(jù)配置文件中的map指令和后端服務(wù)器的配置進(jìn)行拼接的,例如https://$host$request_uri表示將請求轉(zhuǎn)發(fā)到https協(xié)議下的當(dāng)前域名,并保留原始請求的URI路徑。
因此,當(dāng)使用以下命令發(fā)送請求時(shí):
??
curl?-v?-H?'content-type:?aplication/json'?-H?'PROT:?https'?-H?'DN:?www.test.com'?-H?'Port:?8899'?-d?'{"data":?"xxxx",?"body":"1"}'??http://nginx_ip:4480/test/uri
請求將被轉(zhuǎn)發(fā)到$backend_4480_svr,并根據(jù)請求頭中的PROT、DN和Port字段的值拼接成后端服務(wù)器的地址,同時(shí)將請求頭中的Host字段、PROT字段、DN字段和Port字段傳遞給后端服務(wù)器。具體的轉(zhuǎn)發(fā)地址會(huì)根據(jù)配置文件中的map指令和后端服務(wù)器的配置進(jìn)行動(dòng)態(tài)生成。