Nginx 作為 WebSockets 代理

jopen 10年前發布 | 34K 次閱讀 Nginx Web服務器

WebSocket 協議給我們提供了一個創建可以支持客戶端和服務端進行雙向實時通信的web應用程序的方法。相比之前使用的方法,WebSocket(作為HTML5的一部分)可以使我們更容易開的發出這種類型的應用程序。絕大多數的現代瀏覽器都支持WebSocket,包括火狐,IE,Chrome,Safari以及 Opera等,同時,越來越多的服務端框架也開始支持WebSocket了。

對于企業應用來說,我們需要多個WebSocket服務器來保障性能和高可用性,因此我們迫切的需要對WebSocket協議進行負載均衡。NGINX自從1.3版本就開始支持WebSocket了,并且可以為WebSocket應用程序做反向代理負載均衡

WebSocket 和HTTP協議不同,但是WebSocket中的握手和HTTP中的握手兼容,它使用HTTP中的Upgrade協議頭將連接從HTTP升級到 WebSocket。這使得WebSocket程序可以更容易的使用現已存在的基礎設施。例如,WebSocket可以使用標準的HTTP端口 80 和 443,因此,現存的防火墻規則也同樣適用。

一個WebSockets的應用程序會在客戶端和服務端保持一個長時間工作的連接。用來將連接從HTTP升級到WebSocket的HTTP升級機制使用HTTP的Upgrade和Connection協議頭。反向代理服務器在支持WebSocket方面面臨著一些挑戰。一項挑戰是 WebSocket是一個hop-by-hop協議,所以,當代理服務器攔截到一個客戶端發來的Upgrade請求時,它(指服務器)需要將它自己的 Upgrade請求發送給后端服務器,也包括合適的請求頭。此外,由于WebSocket連接是長時間保持的,所以代理服務器需要允許這些連接處于打開狀態,而不是像對待HTTP使用的短連接那樣將其關閉。

NGINX 通過在客戶端和后端服務器之間建立起一條隧道來支持WebSocket。為了使NGINX可以將來自客戶端的Upgrade請求發送給后端服務器,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做代理的實例。這個例子將會使用node.js上的一個實現了WebSocket的模塊——ws。這個示例在Ubuntu 13.10 和 CentOS 6.5上測試通過,但對于其他系統來說也許需要稍作修改。就這個例子來說,WebSocket服務器的IP地址是 192.168.100.10,NGINX服務器的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,我們將會把它當做我們的客戶端,但是我們需要創建一個程序來做我們的服務端。將下面的代碼保存到一個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”,之后監聽8010端口,等待客戶端的連接。它會處理收到的所有請求,并且將接收到的消息輸出在控制臺,之后向客戶端返回一條包含該消息的消息。我們希望NGINX去代理這些請求,通過下面的配置便可實現:

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監聽8020端口,并把接收到的任何請求傳遞給后端的WebSocket服務器以便恰當的處理WebSocket協議。我們可以使用wscat作為客戶端來測試一下:

/root/node_modules/ws/bin/wscat –connect ws://192.168.100.20:8020

上面的命令會通過NGINX代理服務器和WebSocket服務器建立連接,你可以輸入你想要發送給服務器的消息,之后服務器會返回一條消息。每當你輸入一條消息,你應該可以在服務端看到該消息的輸出,之后在客戶端會顯示一條來自服務端的消息。

這是一個交互示例:

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

由此我們可以看到服務端與客戶端能夠通過作為代理的NGINX通信, 而且消息可以持續進行雙向傳輸直到客戶端或服務端斷開連接。為了能使NGINX正確處理WebSocket, 只需正確地設置消息頭來處理更新從http到WebSocket連接的Upgrade請求。

更多信息請參見:

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!