要問目前哪個Web服務(wù)器最流行,可能大家都會說是Nginx。這個老毛子開發(fā)的神器憑借高性能了、友好的配置、優(yōu)秀的模塊機制,反向代理支持迅速占領(lǐng)市場并走上Web服務(wù)器市場的No1. 本文我們就來談?wù)剬τ陂_發(fā)人員來說Nginx的一些妙用方法。
從這個簡單的配置,我們可以開始構(gòu)建必要的復(fù)雜性。
主動/被動代理配置
如果服務(wù)需要以一臺服務(wù)器為主的主動/被動配置 對于請求處理,將一個后端服務(wù)器做備份,配置方法如下:
...
upstream backend {
server job:10000;
server backup:10000 backup;
}
...
Backup選項指示,表示nginx將使用該服務(wù)器為備用服務(wù)器,只有當主服務(wù)器(job:10000)不可用時候,才會代理到backup:10000。
默認情況下,服務(wù)器在1次連接錯誤或超時后標記為不可用。但是該閾值可以通過max_fails來配置:
...
upstream backend {
# 主服務(wù)器失敗嘗試3次
server job:10000 max_fails=3;
# 備用服務(wù)器嘗試失敗次數(shù)10
server backup:10000 backup max_fails=10;
}
...
除了連接錯誤和超時,還可以使用HTTP 狀態(tài)碼,比如500,502等來定義不可用狀態(tài)。該配置,由proxy_next_upstream語句定義。
...
upstream backend {
server job:10000;
server backup:10000 backup;
}
server {
server_name proxy;
listen 8000;
# 在連接錯誤,超時或者http 429(請求超出)的時候時候,跳轉(zhuǎn)到下一個pstream。
proxy_next_upstream error timeout http_429;
location / {
proxy_pass http://backend;
}
}
...
代理K8s服務(wù)
如果要代理K8s集群服務(wù),由于其自帶負載均衡配置中應(yīng)該設(shè)置max_fails=0,以免踢掉正常的服務(wù)器:
...
upstream backend {
server cck8s.svc max_fails=0;
}
...
這樣nginx就不會將 K8s服務(wù)標記為不可用。即不會去嘗試做被動健康檢查,直接利用的K8s集群的主動健康檢查。
靈活的路由 map
有時需要根據(jù)某些HTTP標頭值路由請求。比如查詢范圍、cookie值或、主機名或者他們的任意組合。這是Nginx最強大的地方,其他同類的代理基本上都做不到這一點。Nginx請求路由的關(guān)鍵是ngx_http_map_module。該模塊允許從組合中定義變量帶有正則表達式的變量。假設(shè)微服務(wù)架構(gòu)中有3個后端服務(wù)來處理不同類型的數(shù)據(jù):
返回剛剛收集的最新數(shù)據(jù)的實時數(shù)據(jù)服務(wù)。
返回舊數(shù)據(jù)的歷史數(shù)據(jù)服務(wù)。
返回預(yù)先計算的數(shù)據(jù)的聚合數(shù)據(jù)服務(wù)。
這些服務(wù)通過相同的接口提供用戶服務(wù),接口形式如下
<date>.cc.api/?report=<report>
現(xiàn)在有幾個用戶用例:
2021-04-01.cc.api/?report=list_records 應(yīng)該提供歷史數(shù)據(jù)服務(wù)。
cc.api/?report=list_records 應(yīng)該提供實時數(shù)據(jù)服務(wù)。
cc.api/?report=counters提供聚合數(shù)據(jù)服務(wù)。
2018-11-01.cc.api/?report=counters 提供歷史聚合數(shù)據(jù)服務(wù)。
如果自己在程序中自己寫路由則非常復(fù)雜,而用Nginx可以幫你通過很少配置就搞定:
首先,定義3個后端服務(wù)器:
upstream live {
server live1:8000;
server live2:8000;
server live3:8000;
}
upstream hist {
server hist1:9999;
server hist2:9999;
}
upstream agg {
server agg1:7100;
server agg2:7100;
server agg3:7100;
}
接下來,定義將偵聽所有請求并以某種方式路由提供服務(wù):
server {
server_name *.cc.api "";
listen 80;
location / {
proxy_pass http://???;
}
}
問題是我們應(yīng)該寫什么proxy_pass指示?
由于nginx配置是聲明性的,可以編寫 proxy_pass http://$destination/并使用map構(gòu)建目標變量。
示例服務(wù)中,根據(jù)report請求變量和日期子域名,提取到變量中的內(nèi)容為:
map $host $date {
"~^((?<subdomain>d{4}-d{2}-d{2}).)?cc.api$" $subdomain;
default "";
}
Map會通過正則解析$host變量,并將解析結(jié)果設(shè)置$date。
此處主要使用了有2條map規(guī)則 :主要的規(guī)則是正則表達式,另一個是后備表示為default關(guān)鍵詞。
正則表示式子,我們不在詳細介紹,如果有興趣可以搜索蟲蟲以前的文章。此處規(guī)則為^開頭,提取時間子域名:
d{4}-d{2}-d{2}解析日期格式 2021-06-28。
?<subdomain>被稱為捕獲組,它只是為匹配的部分命名正則表達式。然后在映射規(guī)則的右側(cè)使用捕獲組來分配。$date多變,而且子域是可選的,因此需要 用圓括號括起來(子域分隔符)并在整個組前添加?。
要提取report,無需使用map,nginx的arg_<param>查詢參數(shù)的是預(yù)定義變量。 可以直接用$arg_report。
好的,有了日期report告。接著構(gòu)建 $destination變量,用另一個map,訣竅是在后面map中可以利用上面的變量(包括自定義的)創(chuàng)建新變量的變量組合:
map "$arg_report:$date" $destination {
"~counters:.*" agg;
"~.*:.+" hist;
default live;
}
這里的組合是一個字符串,其中2個變量用冒號連接。冒號是個人選擇,用于方便。實際上可以使用任何符號,只要確保正則表達式是明確的。
在map,有3個規(guī)則。
首先是設(shè)置$destination到gg時report查詢參數(shù)是 counters。
二是設(shè)置 $destination到 hist時,$date變量不為空。
當沒有其他匹配時設(shè)置的默認值是設(shè)置$destination至 live。
map中的正則表達式按順序進行評估。
注意 $destinationvalue 是代理后端的名稱。
完整的配置如下:
events {}
http {
upstream live {
server live1:8000;
server live2:8000;
server live3:8000;
}
upstream hist {
server hist1:9999;
server hist2:9999;
}
upstream agg {
server agg1:7100;
server agg2:7100;
server agg3:7100;
}
map $host $date {
"~^((?<subdomain>d{4}-d{2}-d{2}).)?cc.api$" $subdomain;
default "";
}
map "$arg_report:$date" $destination {
"~counters:.*" agg;
"~.*:.+" hist;
default live;
}
server {
server_name *.cc.api "";
listen 80;
location / {
proxy_pass http://$destination/;
}
}
}
轉(zhuǎn)發(fā)請求給Consul服務(wù)
如果使用Consul進行服務(wù)發(fā)現(xiàn),則可以通過以下方式訪DNS。 簡單的
curl myApp.service.consul
非常方便,但沒有人知道如何解析名稱 .consul zone。
無論如何,要通過 Consul DNS 在 nginx 中路由請求很一件。nginx有一個 resolver模塊nginx 中用于使用自定義 DNS 服務(wù)器的指令(注意就是最近爆嚴重漏洞的那個高危寫入漏洞的CVE-2021-23017的模塊,記得升級哦)。
以下下面是配置Nginx轉(zhuǎn)發(fā)DNS請求給Consul配置:
...
server {
server_name *.cc.api "";
listen 80;
# 解析使用Consul DNS. 遞歸請求到114和谷歌DNS.
resolver 10.0.0.1:8600 10.0.0.2:8600 10.0.0.3:8600 114.114.114.114 8.8.8.8
location /v1/api {
proxy_pass http://prod.cc.api.consul/;
}
location /v1/rpc {
proxy_pass http://prod.rpc.service.consul/;
}
}
...
結(jié)論
Nginx 是一個非常通用的工具。 它有豐富的配置語言為開發(fā)人員提供不錯的功能。比如具有配置故障轉(zhuǎn)移的主動/被動負載平衡、靈活的請求路由與Consul DNS 輕松集成等。雖然有些配置比較丑,甚至使用了可怕的正則,但是這也許就是他的魅力所在吧。