Nginx 的反向代理
Nginx 最強大的地方是在于其 HTTP 請求的反向代理,也即常說的七層反向代理。在這一層代理中,通過 Nginx 框架提供的相關(guān)配置,我們能在該層將發(fā)送過來的 http 協(xié)議轉(zhuǎn)換成各種其他的協(xié)議比如 fastcgi 協(xié)議、uwsgi協(xié)議、grpc、http(高版本協(xié)議)、websocket協(xié)議等。這樣使用 Nginx 框架,我們可以支持多種應(yīng)用服務(wù)(java web、python web等)的反向代理。
Nginx 從1.9.0開始,新增加了一個 stream 模塊,用來實現(xiàn)四層協(xié)議( TCP 或 UDP 協(xié)議)的轉(zhuǎn)發(fā)、代理或者負(fù)載均衡。這層比較簡單,只是單純將 TCP 或 UDP 層的流量轉(zhuǎn)發(fā)到上游服務(wù)器中。接下來,我們將分別介紹這兩種反向代理的基本用法。
1. Nginx 的四層反向代理
前面我們剛開始使用 Nginx 時只是用了 http 指令塊,因為是針對 http 請求進(jìn)行處理。這進(jìn)行的是四層反向代理,轉(zhuǎn)發(fā) TCP 或者 UDP 協(xié)議的報文。針對該層的處理,Nginx 是使用了 stream 模塊進(jìn)行處理,對應(yīng)的是 stream 指令塊,它和 http 指令塊非類似,用法幾乎一致。stream 指令塊里面可以包含 server 指令塊,server 指令塊里面也可以包含 listen 指令塊指定監(jiān)聽的端口、還可以包含 proxy_pass
指令,對該端口監(jiān)聽的 tcp 或者 udp 報文進(jìn)行轉(zhuǎn)發(fā)??傊?,和 http 指令塊大部分用法一致。
...
stream {
...
server {
listen 3440;
# 轉(zhuǎn)發(fā)四層流量
proxy_pass 192.168.1.103:3440;
}
...
}
...
此外,從nginx-1.11.2版開始, ngx_stream_core_module,也同 http 模塊一樣支持變量,部分支持變量如下,這和 http 模塊也是類似的,甚至連變量名都非常相似:
$binary_remote_addr: 二進(jìn)制格式的客戶端地址
$bytes_received: 從客戶端接收到的字節(jié)數(shù)
$bytes_sent: 發(fā)往客戶端的字節(jié)數(shù)
$hostname: 連接域名
$msec: 毫秒精度的當(dāng)前時間
$nginx_version: nginx的版本
$pid: worker進(jìn)程號
$protocol: 通信協(xié)議(UDP or TCP)
$remote_addr: 客戶端ip
$remote_port: 客戶端端口
$server_addr: 接受連接的服務(wù)器ip,計算此變量需要一次系統(tǒng)調(diào)用。所以避免系統(tǒng)調(diào)用,在listen指令里必須指定具體的服務(wù)器地址并且使用參數(shù)bind。
$server_port: 接受連接的服務(wù)器端口
$session_time: 毫秒精度的會話時間(版本1.11.4開始)
$status: 會話狀態(tài)(版本1.11.4開始), 可以是一下幾個值:
200
成功
400
不能正常解析客戶端數(shù)據(jù)
403
禁止訪問
500
服務(wù)器內(nèi)部錯誤
502
網(wǎng)關(guān)錯誤,比如上游服務(wù)器無法連接
503
服務(wù)不可用,比如由于限制連接等措施導(dǎo)致
$time_iso8601: ISO 8601時間格式
$time_local: 普通日志格式的時間戳
2. Nginx 七層反向代理
2.1 http協(xié)議的反向代理
nginx 七層方向代理處理的是 http 請求,對應(yīng)的是 http 協(xié)議。如果只是轉(zhuǎn)發(fā) http 請求,可以簡單使用 proxy_pass 指令即可。這和我們之前簡單的反向代理示例一致。
# 在轉(zhuǎn)發(fā) http 請求時,URL必須以 http 或者 https 開頭
Syntax: proxy_pass URL;
Default: —
Context: location, if in location, limit_except
在使用 prxoy_pass 指令對 http 或者 https 協(xié)議進(jìn)行反向代理時,需要注意一下問題:
- 在 URL 不攜帶 URI 時,會將對應(yīng)的 URL 直接轉(zhuǎn)發(fā)到上游服務(wù)器
- 在 URL 攜帶 URI 時,會將 location 參數(shù)中匹配上的那一段替換為該URL
看下面的示例配置:
...
http {
server {
listen 8000;
location /test {
proxy_pass http://ip:port/xyz;
}
}
server {
listen 9000;
location /test {
proxy_pass http://ip:port;
}
}
}
...
在代理 http 請求 http://主機ip:8000/test/abc 時,由于 proxy_pass 指令后面的 URL 帶了 /xyz 這樣的路徑,所以轉(zhuǎn)發(fā)的請求是 http:// ip:port/xyz/abc,其中匹配的 /test 已經(jīng)被替換掉了。而對于第二個 http 請求 http://主機ip:9000/test/abc 時,由于 proxy_pass指令后面直接是上游服務(wù)器地址,沒有帶 URI,所以轉(zhuǎn)發(fā)的請求為 http://ip:port/test/abc,其中匹配到的/test并沒有被替換。
3. 案例實戰(zhàn)
本節(jié)實戰(zhàn)中,我們準(zhǔn)備好兩個案例測試,一個是測試使用 stream 模塊進(jìn)行四層方向代理測試;另一個案例測試前面提到的七層代理中 proxy_pass 指令的用法,主要實戰(zhàn)前面提到的注意點。
3.1 四層反向代理示例
在nginx.conf的中加入如下配置:
...
stream {
server {
listen 3000;
return '3000 server get ip: $remote_addr!\n';
}
server {
listen 30;
# 注意,只寫ip和port,不要加上[http:]之類的,這里是四層的協(xié)議
proxy_pass 127.0.0.1:3000;
}
}
...
啟動 nginx 后,我們在外面通過 telnet 命令訪問該主機的 3000 和 30端口,可以看到如下結(jié)果:
[shen@shen ~]$ telnet 180.76.152.113 3000
Trying 180.76.152.113...
Connected to 180.76.152.113.
Escape character is '^]'.
3000 server get ip: 103.46.244.108!
[shen@shen ~]$ telnet 180.76.152.113 30
Trying 180.76.152.113...
Connected to 180.76.152.113.
Escape character is '^]'.
3000 server get ip: 127.0.0.1!
Connection closed by foreign host.
可以看到,訪問30端口時,nginx 幫我們轉(zhuǎn)發(fā) tcp 層流量到 3000端口,最后返回了相關(guān)響應(yīng)字符串。
3.2 七層反向代理示例
在 nginx.conf 中加入如下的測配置:
...
http {
server {
listen 8000;
return 200 '$uri\n';
}
server {
listen 9001;
location /test {
proxy_pass http://127.0.0.1;
}
}
server {
listen 9002;
location /test {
proxy_pass http://127.0.0.1/xyz;
}
}
}
...
啟動 nginx 后,通過請求服務(wù)器的8000端口,我們可以知道請求的 uri 值,然后測試經(jīng)過兩種類型的 proxy_pass 配置后最后的 uri 的值。具體操作以及結(jié)果如下:
# 測試8000端口顯示的uri值
[shen@shen ~]$ curl http://180.76.152.113:8000/test/abc
/test/abc
# 9001端口配置中proxy_pass后面的URL不帶URI
[shen@shen ~]$ curl http://180.76.152.113:9001/test/abc
/test/abc
# 9002端口配置中proxy_pass后面的URL帶URI,會替換到匹配的/test
[shen@shen ~]$ curl http://180.76.152.113:9002/test/abc
/xyz/abc
4. 小結(jié)
本節(jié)中介紹了 Nginx 的四層反向代理和七層反向代理,并用案例進(jìn)行了演示。剛開始使用時只是用了 http 指令塊,因為是針對 http 請求進(jìn)行處理。這進(jìn)行的是四層反向代理。nginx 七層方向代理處理的是 http 請求,對應(yīng)的是 http 協(xié)議。如果只是轉(zhuǎn)發(fā) http 請求,可以簡單使用 proxy_pass 指令即可。這和我們之前簡單的反向代理示例一致。