Nginx擔當WebSockets代理
英文原文:http://nginx.com/blog/websocket-nginx/
作者:chszs,轉載需注明。
WebSocket 協議提供了一種創建支持client和服務端實時雙向通信Web應用程序的方法。作為HTML5規范的一部分,WebSockets簡化了開發Web實時通信程序的難度。眼下主流的瀏覽器都支持WebSockets,包含火狐、IE、Chrome、Safari以及Opera等,並且,越來越多的server應用框架也開始支持WebSockets。
要在企業產品中使用WebSockets,為滿足高性能和高可用性,須要多個WebSocketserver。負載均衡層須要支持WebSocket協議。
Nginx從1.3版起就開始支持WebSocket協議,並且能夠擔當WebSocket應用程序的反向代理以及實現負載均衡。
WebSocket協議和HTTP協議不同,可是WebSocket協議的握手和HTTP是兼容的,它使用HTTP的Upgrade協議頭將連接從HTTP連接升級到WebSocket連接。
這個特性使得WebSocket應用程序能夠非常easy地應用到現有的基礎設施。比如,WebSocket應用能夠使用標准的80和443 HTTPport,因此能夠通過現有的防火牆設施。
WebSockets應用程序會在client和server之間建立一個長連接,使得開發實時應用非常easy。
HTTP的Upgrade協議頭機制用於將連接從HTTP連接升級到WebSocket連接。Upgrade機制使用了Upgrade協議頭和Connection協議頭。反向代理server在支持WebSocket協議方面面臨着一些挑戰。挑戰之中的一個是WebSocket是一個逐段轉發(hop-by-hop)協議。因此當代理server攔截到來自client的Upgrade請求時,代理server須要將自己的Upgrade請求發送給后端server,包含適合的請求頭。並且。由於WebSocket連接是長連接,與傳統的HTTP端連接截然不同,故反向代理server還須要同意這些連接處於打開(Open)狀態,而不能由於其空暇就關閉了連接。
Nginx通過在client和后端server之間建立隧道來支持WebSockets通信。為了讓Nginx能夠將來自client的Upgrade請求發送到后端server。Upgrade和Connection的頭信息必須被顯式的設置。
例如以下所看到的:
location /wsapp/ { proxy_pass http://wsbackend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
一旦我們完畢以上設置,Nginx就能夠處理WebSocket連接了。
Nginx WebSockets 實例
以下的樣例講述了Nginx是怎樣為WebSocket做代理的。
此例將使用ws模塊,它是基於node.js構建的WebSocket實現。Nginx將擔當反向代理server,后端server是一個使用了ws和Node.js的簡單WebSockets應用。樣例使用的命令在Ubuntu 13.10和CentOS 6.5上測試通過。但對於其它操作系統也許須要稍作改動。
就這個樣例來說,WebSocketserver的IP地址是192.168.100.10,Nginxserver的IP地址是192.168.100.20。
假設你還沒有安裝node.js和npm,你能夠通過以下命令安裝:
對 Debian/Ubuntu 來說:
sudo apt-get install nodejs npm
對 RHEL/CentOS 來說:
sudo yum install nodejs npm
在Ubuntu上。node.js會被安裝為"nodejs",但在CentOS中被會安裝為"node"。我們在樣例中統一使用"node"。所以,我們會在Ubuntu上創建一個符號連接來同意我們使用“node”:
ln -s /usr/bin/nodejs /usr/local/bin/node
然后安裝 ws:
sudo npm install ws
注意:假設你得到了一個錯誤:“Error: failed to fetch from registry: ws” ,那么執行以下的命令應該能解決問題:
sudo npm config set registry http://registry.npmjs.org/
接下來,你能夠再次執行 sudo npm install ws
ws模塊來自/root/node_modules/ws/bin/wscat,我們會為client使用它,可是我們須要創建一個程序來作為我們的server。將以下的代碼保存到一個server.js文件里:
console.log("Server started"); var Msg = ''; var WebSocketServer = require('ws').Server , wss = new WebSocketServer({port: 8010}); wss.on('connection', function(ws) { ws.on('message', function(message) { console.log('Received from client: %s', message); ws.send('Server received from client: ' + message); }); });
這個程序能夠通過以下的命令執行:
node server.js
該程序會輸出一條初始化消息“Server started”,之后監聽8010port。等待client的連接。
它會處理收到的全部請求,並且將接收到的消息輸出在控制台。之后向client返回一條包含該消息的消息。我們希望Nginx去代理client的請求,能夠通過以下的配置實現:
map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream websocket { server 192.168.100.10:8010; } server { listen 8020; location / { proxy_pass http://websocket; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } }
上面的配置會使Nginx監聽8020port,並把不論什么接收到的請求轉發給后端的WebSocketserver。讓后端server更好地處理WebSocket協議。
我們能夠使用wscat作為client來測試一下:
/root/node_modules/ws/bin/wscat –connect ws://192.168.100.20:8020
上面的命令會通過Nginx反向代理server和后端WebSocketserver建立連接,你能夠向server發送隨意消息,然后server會返回一條消息。每當你在client發送一條消息,在后端server上能看到該消息的輸出,之后在client會顯示一條來自服務端的消息。
這是一個交互演示樣例:
Server: |
Client: |
$ node server.js |
|
Server started |
|
|
wscat –connect ws://192.168.100.20:8020 |
|
Connected (press CTRL+C to quit) |
|
> Hello |
Received from client: Hello |
|
|
< Server received from client: Hello |
由此我們能夠看到client與server能通過Nginx反向代理建立WebSockets通信,並且消息能夠持續地進行雙向傳輸。直至client或server斷開連接。為了讓Nginx能正確處理WebSocket連接,僅僅需正確地設置消息頭來處理從HTTP連接升級到WebSocket連接的Upgrade請求。