node之時鐘web伺服器綜合案例
原理
一般我們開發的WebSocket服務程式使用ws協議,明文的。但是怎樣讓它安全的通過網際網路傳輸呢?這時候可以通過nginx在客戶端和服務端直接做一個轉發了, 客戶端通過wss訪問,然後nginx和服務端通過ws協議通訊。如下圖所示:
先直接展示配置檔案,如下所示(使用的話直接複製,然後改改ip和port即可)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream wsbackend{
server ip1:port1;
server ip2:port2;
keepalive 1000;
}
server {
listen 20038;
location /{
proxy_http_version 1.1;
proxy_pass http://wsbackend;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
接下來,我們就分別分析上述配置的具體含義。
首先:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
表示的是:
如果 $http_upgrade 不為 '' (空),則 $connection_upgrade 為 upgrade 。
如果 $http_upgrade 為 '' (空),則 $connection_upgrade 為 close。
其次:
upstream wsbackend{
server ip1:port1;
server ip2:port2;
keepalive 1000;
}
表示的是 nginx負載均衡:
兩臺伺服器 (ip1:port1)和(ip2:port2) 。
keepalive 1000 表示的是每個nginx程序中上游伺服器保持的空閒連線,當空閒連線過多時,會關閉最少使用的空閒連線.當然,這不是限制連線總數的,可以想象成空閒連線池的大小,設定的值應該是上游伺服器能夠承受的。
最後:
server {
listen 20038;
location /{
proxy_http_version 1.1;
proxy_pass http://wsbackend;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
表示的是監聽的伺服器的配置
listen 20038 表示 nginx 監聽的埠
locations / 表示監聽的路徑(/表示所有路徑,通用匹配,相當於default)
proxt_http_version 1.1 表示反向代理髮送的HTTP協議的版本是1.1,HTTP1.1支援長連線
proxy_pass http://wsbackend; 表示反向代理的uri,這裡可以使用負載均衡變數
proxy_redirect off; 表示不要替換路徑,其實這裡如果是/則有沒有都沒關係,因為default也是將路徑替換到proxy_pass的後邊
proxy_set_header Host $host; 表示傳遞時請求頭不變, $host是nginx內建變數,表示的是當前的請求頭,proxy_set_header表示設定請求頭
proxy_set_header X-Real-IP $remote_addr; 表示傳遞時來源的ip還是現在的客戶端的ip
proxy_read_timeout 3600s; 表的兩次請求之間的間隔超過 3600s 後才關閉這個連線,預設的60s,自動關閉的元凶
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 表示X-Forwarded-For頭不發生改變
proxy_set_header Upgrade $http_upgrade; 表示設定Upgrade不變
proxy_set_header Connection $connection_upgrade; 表示如果 $http_upgrade為upgrade,則請求為upgrade(websocket),如果不是,就關閉連線
若是實際使用的websocket地址後面還有路徑,比如實際地址是這樣的,訪問地址是 http://www.a.com,實際會轉發給ws://127.0.0.1:8094/ws
Nginx如何支援WebSocket
WebSocket 和HTTP雖然是不同協議,但是兩者“握手”方式相容。通過HTTP升級機制,使用HTTP的Upgrade和Connection協議頭的方式可以將連線從HTTP升級為WebSocket。如下圖所示
Http升級為WebSocket
因為WebSocket協議是一個hop-by-hop協議(此類頭部欄位只對單次轉發有效。會因為轉發給快取/代理伺服器而失效),為了讓Nginx代理伺服器可以將來自客戶端的Upgrade請求傳送到後端伺服器,要求Upgrade和Connection的頭資訊必須被顯式的設定。可以通過下文將要講解的修改Nginx的配置檔案方式解決此問題。
Nginx配置Websocket方式
通過修改nginx.conf配置,如下圖所示
1.map是根據客戶端請求中 $http_upgrade 的值來構造改變 $connection_upgrade 的值,即根據變數 $http_upgrade 的值和{} 裡規則建立新的變數 $connection_upgrade並賦值。
2.HTTP的Upgrade協議頭機制用於將連線從HTTP連線升級到WebSocket連線,Upgrade機制使用了Upgrade協議頭和Connection協議頭;為了讓Nginx可以將來自客戶端的Upgrade請求傳送到後端伺服器,Upgrade和Connection的頭資訊必須被顯式的設定。如上圖程式碼中第三個紅圈。
總結
通過Http升級機制將Http升級為WebSocket。Nginx代理伺服器通過修改配置的方式解決了WebSocket屬於hop-by-hop協議的問題、並通過保持分別與客戶端和服務端的連線一直處於開啟狀態從而實現WebSokcet的代理。