websocket連接的后台反向代理問題


今天要介紹的問題,是一個相對來說比較經典的問題,問題表面看不是很復雜的問題,但是反映出的背后通信邏輯,其實還是比較有意義的。

 

websocket協議是當前絕大部分瀏覽器都支持的長連接協議,是HTTP協議的升級版協議,解決HTTP協議的單向通信的弊端,WS(websocket協議的簡寫)協議,基於長連接,實現客戶端和服務端之間的雙向通信,這個技術在我們項目里面,主要用來解決客服系統問題,即客戶發送的消息以及坐席回復的消息之間,可以實時交互。關於這個協議的更多介紹,請參考官方網站,這里不多做介紹。

 

下面說下我們項目中,研究websocket連接,在集群環境下,如何解決后端tomcat上只有一個連接實例問題。我們的實驗環境,topo架構如下:

 

結合上面的topo架構,客戶端的請求,只有在涉及到websocket的連接的時候,才會從Client LB一直走到Server LB的后面tomcat上。除此之外,其他的客戶端請求,只會到client app對應的tomcat即停止。

 

0.環境信息
1).客戶端后台nginx,充當Client LB 
這里的客戶端,主要指類似聊天窗的渲染以及基本的和客戶端相關的邏輯主體。我們的實驗中,客戶端,主要就是一個聊天窗口和附着在這個上面的輔助應用。

[root@localhost sbin]# ./nginx -V
nginx version: openresty/1.11.2.2
built by gcc 4.1.2 20080704 (Red Hat 4.1.2-51)
built with OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008
TLS SNI support disabled
configure arguments: --prefix=/usr/local/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.60 --add-module=../xss-nginx-module-0.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.31 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.06 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.7 --add-module=../ngx_lua_upstream-0.06 --add-module=../headers-more-nginx-module-0.32 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.17 --add-module=../redis2-nginx-module-0.13 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.14 --add-module=../rds-csv-nginx-module-0.07 --with-ld-opt=-Wl,-rpath,/usr/local/luajit/lib --with-http_realip_module --with-pcre --add-module=/usr/local/openresty-1.11.2.2/bundle/ngx_cache_purge-2.3 --add-module=/usr/local/openresty-1.11.2.2/bundle/nginx_upstream_check_module-0.3.0 --with-http_ssl_module

2)服務端后台nginx,充當Server LB
這里主要指websocket消息的通信處理邏輯,即客戶端收到客戶的消息后要通過客戶端后台反向代理推送給的目的服務地址。

[root@bogon nginx]# ./sbin/nginx -V
nginx version: openresty/1.11.2.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) 
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.60 --add-module=../xss-nginx-module-0.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.31 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.06 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.7 --add-module=../ngx_lua_upstream-0.06 --add-module=../headers-more-nginx-module-0.32 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.17 --add-module=../redis2-nginx-module-0.13 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.14 --add-module=../rds-csv-nginx-module-0.07 --with-ld-opt=-Wl,-rpath,/usr/local/openresty/luajit/lib --add-module=/opt/nginx-rtmp-module-master --with-http_ssl_module

 

1.客戶端應用對應的后台nginx的反向代理配置

upstream ims_client {
   server 10.90.9.20:7080;
   server 10.90.9.20:8080;
}       
upstream ims_svr {
   server 10.90.7.10:9091;
} 
server {  
    listen  9090;
    server_name localhost;
    default_type text/html; 
    proxy_send_timeout   3000;
    proxy_read_timeout   3000;

    location /IMS_SVR/websocket { proxy_pass http://ims_svr; proxy_set_header Host $host:$server_port; proxy_set_header Remote_Addr $remote_addr; 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 "upgrade"; }
    location /IMS {
        proxy_pass http://ims_client;
        proxy_set_header Host $host:$server_port;
        proxy_set_header Remote_Addr $remote_addr;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    }
}

對應的客戶端應用,在我本地開發機器上,啟動了兩個tomcat,一個端口7080,另外一個8080,也就是上面nginx配置中的ims_client部分,當客戶端請求IMS這個客戶端應用時,客戶端的界面渲染以及在客戶端本地能完成的工作,都在這個應用里面。只有客戶端涉及到和websocket連接有關的請求,會被反向代理到ims_svr對應的服務入口上。如上面的nginx配置,ims_svr對應的服務入口,其實就是websocket后端服務端的負載均衡入口地址,這里10.90.7.10:9091其實就是另外一個nginx服務的入口地址。

 

2. 服務端應用對應的后台nginx的反向代理配置

upstream ims_svr {
    server 10.90.9.20:9080;
    server 10.90.9.20:9081;
    #hash $query_string;
    #hash $request_uri;
    #hash $arg_userId;
}
server {
    listen  9091;
    server_name localhost;
    default_type text/html;

    location /IMS_SVR/websocket {
        proxy_pass http://ims_svr;
        proxy_set_header Host $host:$server_port;
        proxy_set_header Remote_Addr $remote_addr;
        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 "upgrade"; }
}

這里,主要是將websocket的請求反向代理到后端具體的tomcat服務器上,websocket的對應tomcat服務器有2個,也是本地基於不同端口啟動的9080和9081兩個tomcat應用。

 

這次實驗的目的,主要是想知道,nginx配置的輪詢的方式做反向代理,WS連接,是否也會在后端服務器之間不停的切換,造成后端tomcat上存在多個連接實例peer,因為長連接通信,是存在兩個對端實例的。若出現tomcat端出現多個客戶端的連接對端peer,那么,就不符合我們業務的需要了,即浪費資源,又不便於邏輯管理。

 

我們的測試過程中,發現,不論客戶端HTML5頁面如何刷新,最終websocket總是在同一個tomcat上。這個是非常重要的結論。另外,當Server 端的tomcat停掉一個,比如9080對應的app停掉,客戶端HTML5,因為websocket頁面js里面添加心跳機制,實現重連,客戶在無感知的情況下,websocket重新連接上來,接到了server app 9081上面來了。

 

客戶端聊天界面如下:

 

通過這次實驗得出如下結論:

1. 基於websocket長連接的通信,可以不用通過客戶id等指定唯一字段信息進行hash來鎖定客戶端和websocket服務端之間的peer到peer的映射關系,這個映射關系,應該是在websocket的配置,即nginx中配置代碼段中紅色部分指定的。

2. 基於websocket的長連接的反向代理,客戶端和服務端的反向代理nginx配置邏輯要一致,保證連接正常通信。

3. 基於websocket的長連接的通信,前端的業務層面的heartbeat心跳機制,是非常有用的,這個保證某個websocket后端服務出現異常的時候,客戶可以無感知的繼續得到服務。


免責聲明!

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



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