1. 背景介紹html
基於websocket的及時通訊中,客戶端與服務端創建ws鏈接後,服務端將業務繼續傳遞到下一級業務服務系統Business server後,下一級服務系統處理完畢後,要將結果反饋給客戶端,而此時的客戶端ws服務器存在多個實例時,處理方式上存在幾種策略:前端
1) 比較常見的是基於ip哈希;nginx
2)基於參數的hash;web
這些,通常狀況下能夠知足業務需求,可是呢,在ws服務動態增減的時候,可能就會出現消息丟失。例如:當前有3臺websocket的tomcat服務器,有一個ws鏈接創建在tomcat1上面,基於輪詢的模式下,且websocket前端採用了心跳機制時,客戶端的重連機制會讓這個客戶端鏈接轉而鏈接到其餘兩個tomcat當中之一,例如tomcat2,而客戶是無感知的, 請參照websocket鏈接的後臺反向代理問題。若這個時候Business server回調及時通訊系統IM後(HTTP),不作特別處理,在輪詢的方式下(或者hash方式),會出現消息去往的websocket的tomcat服務器不是這個回覆消息該去往的tomcat服務器。致使回覆消息沒法到達該客戶的websocket鏈接通道上,從而出現消息丟失的問題。redis
如上圖所示:客戶請求如紅色線條,起初,通過LB-》nginx1-》websocket server1-》other business server. 可是處理過程當中,websocket server1宕機了,或者其餘什麼緣由,不能對外服務了,客戶端的心跳機制,使得websocket重連,連到了websocket server2上了。此時,other business server處理完了客戶的請求,回調IM系統,投遞客戶請求的答案,有可能出現上圖綠色的箭頭所示,這個時候,該客戶的消息是沒有辦法投遞出去的,由於websocket的長鏈接是在客戶端和websocket server2.tomcat
即便還有一種,就是基於redis的訂閱發佈,進行消息回傳給ws服務器,這種作法,要增長redis服務器的壓力,且可能存在屢次發佈操做,性能很差,放棄。服務器
這裏要介紹的是,基於http的接口調用,經過url後面帶上query信息,指定ws所在的服務器的ip和port信息。而後在nginx上作特殊配置,實現消息回傳時,指定調用ws服務器集羣中的服務器,由於這個服務器上存在websocket的鏈接實例。websocket
nginx的配置以下:session
upstream ims_svr { server 10.130.215.143:8080; server 10.130.215.144:8080; } #用於BI回調IMS系統定位消息具體去往那個tomcat服務,涉及websocket的鏈接要原路來還要原路回去 upstream 10.130.215.1438080 { server 10.130.215.143:8080; } upstream 10.130.215.1448080 { server 10.130.215.144:8080; } server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; default_type text/html; location / { root html; index index.html index.htm; } location /IMS { 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/(replyMessage|callback) { proxy_pass http://$arg_ip$arg_port; 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; } }
上面的配置中,技巧就在於獲取query中的ip字段和port字段的值,拼接在一塊兒構成upstream的block名稱。在proxy_pass的代理下,就能夠去往特定服務器,即鏈接有websocket鏈接的那個tomcat上。socket
注意:這裏你可能會說,websocket server一樣會出現宕機啊,在other Business server回調的時候,對的。咱們能夠在other Business server和websocket server之間搭建redis服務器,做爲信息中心節點,經過visistorId+sessionid做爲redis的key,ip_port值做爲這個key對應的鍵值,當other Business server回調IM前,獲取一下這個visistorId在當前sessionid下的websocket創建在那個websocket server上,即獲取IP和Port信息。在回調的HTTP接口的URL部分附着query信息,例如?ip=1.1.1.1&port=8080.
如上圖所示的案例,客戶請求通過LB-》nginx1-》websocket server1-》other business server-》LB-》nginx2-》websocket server1-》resp去往客戶端(基於websocket長鏈接),由於HTTP請求,在LB上是基於輪詢的,因此,HTTP回調的response信息,也多是通過nginx1的,這個時候,基於咱們的nginx配置規則,依然會使得resp反饋去往websocket server1上,保障resp響應必定是在websocket鏈接創建的通道上回復給客戶端,確保resp消息不會丟失。
測試案例截圖:
到此,整個方案介紹完畢,是否是以爲nginx很厲害?的確,nginx如今的web應用領域佔有率很是高。
另外,這裏要注意一個小細節:就是nginx獲取query部分的變量時,$arg_<xxx>這裏的xxx部分,不要出現下劃線「_」了,不然會致使query部分的變量獲取不到,我經歷過這個血的教訓。
例如我開始的配置以下:
upstream ims_svr { server 10.130.215.143:8080; server 10.130.215.144:8080; } #用於BI回調IMS系統定位消息具體去往那個tomcat服務,涉及websocket的鏈接要原路來還要原路回去 upstream 10.130.215.143_8080 { server 10.130.215.143:8080; } upstream 10.130.215.144_8080 { server 10.130.215.144:8080; } server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; default_type text/html; location / { root html; index index.html index.htm; } location /IMS { 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/(replyMessage|callback) { proxy_pass http://$arg_ip_$arg_port; 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; } }
測試過程當中,nginx後臺報錯以下:
access.log
10.130.207.217 - - [23/Mar/2018:13:03:28 +0800] "POST /IMS/replyMessage?ip=10.130.215.143&port=8080 HTTP/1.1" 502 541 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
error.log
2018/03/23 13:03:28 [error] 2358#0: *253758 no resolver defined to resolve 8080, client: 10.130.207.217, server: localhost, request: "POST /IMS/replyMessage?ip=10.130.215.143&port=8080 HTTP/1.1", host: "10.130.207.217"
這裏顯然是$arg_ip_$arg_port值沒有計算對。根本緣由是$arg_ip_搞錯了
切記,切記nginx的query的arg取值規則。