title: 負(fù)載均衡關(guān)于請求頭的那些事
date: 2022/07/21 13:58
本地環(huán)境
瀏覽器/客戶端(127.0.0.1)
# 訪問 nginx
curl localhost:8002/main/dqPlow/rest/async/req
nginx(192.168.2.43:8002)
server {
listen 8002;
server_name localhost;
location / {
proxy_pass http://192.168.2.50:9999;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
root html;
index index.html index.htm;
}
}
gateway(192.168.2.50:9999)
spring:
cloud:
gateway:
routes:
- id: dqPlow
uri: http://192.168.2.50:8080
predicates:
- Path=/main/dqPlow/**
filters:
- StripPrefix=1
dpPlow(192.168.2.50:8080)
請求頭變化
nginx | gateway | 請求頭 | nginx 代理后(gateway 接收到的請求頭信息) | gateway 代理后(應(yīng)用服務(wù)接收到的請求頭信息) |
---|---|---|---|---|
√ | √ | x-forwarded-proto | http | http,http |
√ | √ | x-forwarded-host | localhost:8002 | localhost:8002,192.168.2.50:9999 |
√ | √ | x-forwarded-for | 127.0.0.1 | 127.0.0.1,192.168.2.43 |
√ | √ | x-forwarded-port | 8002 | 8002,9999 |
√ | √ | x-forwarded-prefix | 未配置前綴 | /main |
√ | √ | forwarded | 未配置 | proto=http;host="192.168.2.50:9999";for="192.168.2.43:10593" |
√ | x-real-ip | 127.0.0.1 | 127.0.0.1 | |
√ | √ | host | 192.168.2.50:9999 | 192.168.2.50:8080 |
x-forwarded-proto:標(biāo)識客戶端(瀏覽器或其他負(fù)載均衡器)用于連接到負(fù)載均衡器的實(shí)際協(xié)議虫啥。
x-forwarded-host:【客戶端調(diào)用服務(wù)端所使用的 ip:port】標(biāo)識客戶端(瀏覽器或其他負(fù)載均衡器)用于連接到負(fù)載均衡器的 ip:port
-
x-forwarded-for:【客戶端 ip】在客戶端訪問服務(wù)器的過程中如果需要經(jīng)過 HTTP 代理或者負(fù)載均衡服務(wù)器,可以被用來獲取最初發(fā)起請求的客戶端的 IP 地址垂攘。
- 格式:
X-Forwarded-For: <client>, <proxy1>, <proxy2>
-
<client>
:客戶端的 IP 地址肺缕。 -
<proxy1>, <proxy2>
:如果一個請求經(jīng)過了多個代理服務(wù)器砚作,那么每一個代理服務(wù)器的 IP 地址都會被依次記錄在內(nèi)暮顺。也就是說邓嘹,最右端的 IP 地址表示最近通過的代理服務(wù)器辖所,而最左端的 IP 地址表示最初發(fā)起請求的客戶端的 IP 地址恨课。
- 格式:
x-forwarded-port:標(biāo)識客戶端(瀏覽器或其他負(fù)載均衡器)用于連接到負(fù)載均衡器的端口號舆乔。
x-forwarded-prefix:負(fù)載均衡服務(wù)器代理服務(wù)的前綴
注:上面這幾個頭部不屬于任何一份既有規(guī)范,但是他們已經(jīng)成為事實(shí)上的標(biāo)準(zhǔn)剂公;關(guān)于負(fù)載均衡請求頭的標(biāo)準(zhǔn)版本是
Forwarded
.forwarded:感覺是一個沒用起來的規(guī)范
注:下面這倆可以用希俩,但是不咋建議用
x-real-ip:【nginx 專屬】客戶端真實(shí) ip,與
x-forwarded-for
中的是一樣的host:負(fù)載均衡器調(diào)用服務(wù)使用的 ip:port
纲辽?颜武? 請求頭難道不區(qū)分大小寫嗎
HTTP報頭的名稱是不區(qū)分大小寫贫母,根據(jù)RFC 2616:每個標(biāo)題字段由一個名字后跟一個冒號(“:”)和字段值組成。字段名稱不區(qū)分大小寫(字段值可能區(qū)分大小寫盒刚,也可能不區(qū)分大小寫)腺劣。
注 1:所以 dqPlow 想要獲取用戶訪問的地址的話應(yīng)該這樣拼接:
x-forwarded-proto[0] + :// + x-forwarded-host[0] + x-forwarded-prefix[0...n] + server.servlet.context-path + 拼接自己的路徑
注2:如果在 nginx 中不配置那些 header,調(diào)用時dqPlow 服務(wù)接收到的請求頭是這樣的因块。
|-forwarded : proto=http;host="192.168.2.50:9999";for="192.168.2.43:9889"
|-x-forwarded-for : 192.168.2.43
|-x-forwarded-proto : http
|-x-forwarded-prefix : /main
|-x-forwarded-port : 9999
|-x-forwarded-host : 192.168.2.50:9999
注3:在spring體系中橘原,可以使用 ForwardedHeaderFilter 來處理此事務(wù),它是1個 filter 層的過濾器涡上,將會讀取 X-Forwarded- 層的頭信息趾断,并重新設(shè)置在 相應(yīng)的 request.getXXX 方法上,那么在業(yè)務(wù)端通過上述這些方法即能拿到正確的數(shù)據(jù)吩愧。 同時芋酌,此過濾器也將隱藏X-Forwarded- 這些請求頭,請求信息就像直接從瀏覽器發(fā)過來的一樣的雁佳。這樣一方面避免業(yè)務(wù)端來手動地處理這些請求脐帝,另一方面也避免業(yè)務(wù)端再次讀取這些X-頭,然后進(jìn)行二次處理糖权。
ForwardedHeaderFilter 過濾器并沒有默認(rèn)啟動堵腹,業(yè)務(wù)端需要顯示地進(jìn)行啟用。
需要注意的是星澳,spring容器中有1個組件 UriComponentsBuilder疚顷,以及 ServletUriComponentsBuilder,其內(nèi)部使用了跟 ForwardedHeaderFilter 一樣的邏輯禁偎,但是隨著版本的變更腿堤,其實(shí)際邏輯可能就跟 ForwardedHeaderFilter 邏輯不一致的。因此如暖,在沒有啟用 ForwardedHeaderFilter 的場景中笆檀,請先確保 spring 的版本與當(dāng)前業(yè)務(wù)期望行為一致。如 spring 5.0.X ServletUriComponentsBuilder 會讀取 X-Forwarded-Prefix 頭装处,但spring 5.1.X 就不再處理误债。其在官方的注釋上也寫得清楚浸船,如下
<p><strong>Note:</strong> As of 5.1, methods in this class do not extract``{@code "Forwarded"} and {@code "X-Forwarded-*"} headers that specify the``client-originated address. Please, use``{@link org.springframework.web.filter.ForwardedHeaderFilter``ForwardedHeaderFilter}, or similar from the underlying server, to extract``and use such headers, or to discard them. <p><strong>注意:</strong>從 5.1 開始妄迁,此類中的方法不提取指定客戶端發(fā)起的地址。請使用``{@link org.springframework.web.filter.ForwardedHeaderFilter``ForwardedHeaderFilter} 或來自底層服務(wù)器的類似內(nèi)容李命,以提取``并使用此類標(biāo)頭登淘,或丟棄它們。
并且有特定的 https://github.com/spring-projects/spring-hateoas/issues/862 與之掛接.
在實(shí)際業(yè)務(wù)中封字,如果沒有 啟用 ForwardedHeaderFilter黔州,但想要拿到正確的請求信息耍鬓,建議自己參照 ForwardedHeaderFilter中的類 ForwardedHeaderExtractingRequest,處理好信息流妻,產(chǎn)生uri對象之后牲蜀,再通過 UriComponentsBuilder#fromUri 來拼接地址信息。