原文: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 |
map $http_upgrade $connection_upgrade { |
之類解釋一下關鍵配置部分:
最重要的就是在反向代理的配置中增加了如下兩行,其它的部分和普通的HTTP反向代理沒有任何差別。
1 |
proxy_set_header Upgrade $http_upgrade; |
這里面的關鍵部分在於HTTP的請求中多了如下頭部:
1 |
Upgrade: websocket |
這兩個字段表示請求服務器升級協議為WebSocket。服務器處理完請求后,響應如下報文:
1 |
# 狀態碼為101 |
告訴客戶端已成功切換協議,升級為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 |
map $http_upgrade $connection_upgrade { |
上面唯一要注意的是忙,把proxy_set_header Host $host;
這一行注釋掉了。
而在網關中心主機上面:
1 |
map $http_upgrade $connection_upgrade { |
只需要最外層使用wss協議,里面的交互都使用ws協議,所以監聽80端口即可。