nginx反向代理WebSocket


原文:nginx反向代理WebSocket | 飛污熊博客 (xncoding.com)

WebSocket協議相比較於HTTP協議成功握手后可以多次進行通訊,直到連接被關閉。但是WebSocket中的握手和HTTP中的握手兼容, 它使用HTTP中的Upgrade協議頭將連接從HTTP升級到WebSocket。這使得WebSocket程序可以更容易的使用現已存在的基礎設施。

WebSocket工作在HTTP的80和443端口並使用前綴ws://或者wss://進行協議標注,在建立連接時使用HTTP/1.1的101狀態碼進行協議切換, 當前標准不支持兩個客戶端之間不借助HTTP直接建立Websocket連接。

更多Websocket的介紹可參考我寫的 聊一聊WebSocket 一文。

開發小程序的時候需要用到WebSocket長連接和推送技術,但是必須使用wss,並且必須通過域名訪問。這時候就需要用到nginx反向代理了。

原理

一般我們開發的WebSocket服務程序使用ws協議,明文的。但是怎樣讓它安全的通過互聯網傳輸呢?這時候可以通過nginx在客戶端和服務端直接做一個轉發了, 客戶端通過wss訪問,然后nginx和服務端通過ws協議通信。如下圖所示:

配置

前提條件是你有一個域名,並且申請好了證書。

新建nginx配置文件/etc/nginx/conf.d/websocket.conf,內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

upstream websocket {
server localhost:8282; # appserver_ip:ws_port
}

server {
server_name test.enzhico.net;
listen 443 ssl;
location / {
proxy_pass http://websocket;
proxy_read_timeout 300s;
proxy_send_timeout 300s;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
ssl_certificate /etc/letsencrypt/live/test.enzhico.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/test.enzhico.net/privkey.pem;
}

之類解釋一下關鍵配置部分:

最重要的就是在反向代理的配置中增加了如下兩行,其它的部分和普通的HTTP反向代理沒有任何差別。

1
2
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

這里面的關鍵部分在於HTTP的請求中多了如下頭部:

1
2
Upgrade: websocket
Connection: Upgrade

這兩個字段表示請求服務器升級協議為WebSocket。服務器處理完請求后,響應如下報文:

1
2
3
4
# 狀態碼為101
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade

告訴客戶端已成功切換協議,升級為Websocket協議。握手成功之后,服務器端和客戶端便角色對等,就像普通的Socket一樣,能夠雙向通信。 不再進行HTTP的交互,而是開始WebSocket的數據幀協議實現數據交換。

這里使用map指令可以將變量組合成為新的變量,會根據客戶端傳來的連接中是否帶有Upgrade頭來決定是否給源站傳遞Connection頭, 這樣做的方法比直接全部傳遞upgrade更加優雅。

默認情況下,連接將會在無數據傳輸60秒后關閉,proxy_read_timeout參數可以延長這個時間。源站通過定期發送ping幀以保持連接並確認連接是否還在使用。

兩個超時參數

proxy_read_timeout

語法 proxy_read_timeout time 默認值 60s 上下文 http server location 說明 該指令設置與代理服務器的讀超時時間。它決定了nginx會等待多長時間來獲得請求的響應。 這個時間不是獲得整個response的時間,而是兩次reading操作的時間。

proxy_send_timeout

語法 proxy_send_timeout time 默認值 60s 上下文 http server location 說明 這個指定設置了發送請求給upstream服務器的超時時間。超時設置不是為了整個發送期間,而是在兩次write操作期間。 如果超時后,upstream沒有收到新的數據,nginx會關閉連接

多次代理轉發

工作中遇見過一種情況,就是某個域名在移動網絡下面訪問不了,這樣的話我需要通過一個前段代理服務器做轉發,這樣就涉及到兩次代理。

比如訪問的websocket服務URL為:

1
wss://test.enzhico.net

這個在騰訊雲公網IP上面,所有網絡都能訪問。另外一個域名board.xncoding.com解析到電信網絡,部署在網關中心,這個域名騰訊雲可以訪問到。

在騰訊雲主機上面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {
server_name test.enzhico.net;
location / {
proxy_pass http://board.xncoding.com;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
#proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/test.enzhico.net/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/test.enzhico.net/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

上面唯一要注意的是忙,把proxy_set_header Host $host;這一行注釋掉了。

而在網關中心主機上面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

upstream websocket {
server localhost:8282; # appserver_ip:ws_port
}

server {
listen 80;
server_name board.xncoding.com;
location / {
proxy_pass http://websocket;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}

只需要最外層使用wss協議,里面的交互都使用ws協議,所以監聽80端口即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM