亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

前導(dǎo)

近期有個(gè)同事跟我說(shuō)遇到一件很奇怪的事情,時(shí)不時(shí)收到售后反饋說(shuō) 部分用戶無(wú)法接收到聊天室(WebSocket 服務(wù))消息,然而在測(cè)試服以各種方式測(cè)試都無(wú)法復(fù)現(xiàn)這種現(xiàn)象。于是陷入沉思,因?yàn)檫@個(gè)問(wèn)題必須解決,用戶必須要退出聊天室再重新進(jìn)去才能看到這些丟失的消息,已經(jīng)嚴(yán)重影響到業(yè)務(wù)間客服與用戶的正常溝通。

這到底是什么原因呢?而且沒(méi)法在測(cè)服復(fù)現(xiàn)。

這個(gè)架構(gòu)服務(wù)采用的是 php Swoole , 用戶與客戶端FD 的關(guān)系綁定是通過(guò) Swoole Table (服務(wù)進(jìn)程間內(nèi)存共享) 實(shí)現(xiàn), 同事反映說(shuō)在各個(gè)環(huán)節(jié)確認(rèn)了關(guān)系綁定都沒(méi)問(wèn)題情況下還出現(xiàn) 客戶端FD 丟失,那么我想到 這可能是因?yàn)榉?wù)器被負(fù)載均衡 (SLB)了,無(wú)法測(cè)服復(fù)現(xiàn)是因?yàn)闇y(cè)試服是單機(jī)。

第二天一早, 為了驗(yàn)證猜測(cè),同事查看了在阿里云上的負(fù)載均衡服務(wù)配置,果然破案了!!!這個(gè)項(xiàng)目此前一直是單機(jī)服務(wù),也不知道從何時(shí)開(kāi)始 變成多節(jié)點(diǎn)服務(wù)了。

我來(lái)描述下為什么分布式服務(wù)的 WebSocket 會(huì)存在這種現(xiàn)象,而分布式服務(wù)的 HTTP 卻沒(méi)有這樣的問(wèn)題呢?因?yàn)?nbsp;WebSocket 有個(gè)用戶與客戶端標(biāo)識(shí)(FD)關(guān)系需要綁定,而 HTTP 服務(wù)一般是不需要關(guān)注客戶端標(biāo)識(shí)(FD)的。

WebSocket 服務(wù)端需要推送消息到用戶所連接的客戶端時(shí),例如A、B兩臺(tái)服務(wù)器,用戶1連接到聊天室(服務(wù)器A),客服1也連接到聊天室(服務(wù)器B), 這種情況下 顯然用戶1發(fā)消息給客服1 是對(duì)牛彈琴了,因?yàn)橛脩?發(fā)送消息后,服務(wù)器A會(huì)遍歷該服務(wù)器內(nèi)的所有用戶與客戶端標(biāo)識(shí)(FD),然后取出所有客服1的FD 進(jìn)行消息推送,而客服1連接的是服務(wù)器B,則對(duì)于用戶1來(lái)說(shuō) 客服1是不在線的, 所以用戶1推送消息是推了個(gè)寂寞啊!!! 再如 你的服務(wù)是支持用戶多設(shè)備、多平臺(tái)同時(shí)在線也是一樣的道理,這種情況下也就意味著可能用戶的客戶端標(biāo)識(shí)(FD)會(huì)同時(shí)分布在 服務(wù)器A、服務(wù)器B、服務(wù)器C …,那么用戶在其中一臺(tái)設(shè)備發(fā)送消息,在其他端登陸的該用戶都應(yīng)該要收到這條消息,單純地根據(jù)用戶所連接的服務(wù)去發(fā)送消息 那么其他端在線的該用戶都無(wú)法收到此消息了,群發(fā)也是一樣的道理。

多節(jié)點(diǎn)問(wèn)題

在開(kāi)始思考分布式會(huì)有什么問(wèn)題時(shí),先來(lái)回答一個(gè)問(wèn)題: 服務(wù)端如何與客戶端交流?

在 WebSocket 服務(wù)端,每當(dāng)與客戶端連接成功后,會(huì)生成一個(gè) 唯一的客戶端標(biāo)識(shí)符 FD,WebSocket 會(huì)維護(hù)一個(gè)與客戶端所有連接的 Connections。在業(yè)務(wù)層,你需要將每個(gè)連接進(jìn)來(lái)的客戶端標(biāo)識(shí)(FD)與項(xiàng)目的用戶ID綁定起來(lái),比如用 redis 將用戶和客戶端標(biāo)識(shí)(FD) 保存起來(lái),當(dāng)客戶端斷開(kāi)連接時(shí)解綁(刪除掉對(duì)應(yīng)的客戶端標(biāo)識(shí)(FD)),因?yàn)榉?wù)是用的PHP Swoole, 用 Swoole Table (服務(wù)進(jìn)程間內(nèi)存共享) 實(shí)現(xiàn)用戶與客戶端標(biāo)識(shí)(FD)綁定關(guān)系。這樣你就可以知道某個(gè)用戶在不在線,并且這個(gè)用戶的客戶端標(biāo)識(shí)(FD)有哪些,然后遍歷 Swoole Table 把用戶的所有客戶端標(biāo)識(shí)(FD)取出來(lái)循環(huán)推送消息給客戶端。

那如何給所有人廣播消息呢?

服務(wù)器只需要與它自身的所有客戶端連接 Server.Connections 挨個(gè)發(fā)消息就是廣播,所以它只是一個(gè)偽廣播: 我要給群里所有人發(fā)消息,但我不能在群里發(fā),只能挨個(gè)私發(fā)。

單節(jié)點(diǎn)

當(dāng)單節(jié)點(diǎn)時(shí),流程如下:

 

這時(shí)所有用戶都能收到消息通知。

多節(jié)點(diǎn)

當(dāng)多節(jié)點(diǎn)時(shí),就會(huì)有部分用戶無(wú)法正常收到通知 (就是我文中開(kāi)頭所描述的現(xiàn)象),從以下流程圖中可以很清楚地看到問(wèn)題所在:

 

負(fù)載到節(jié)點(diǎn)B 的所有用戶都沒(méi)有收到消息通知。

如何解決

說(shuō)了這么多,怎么解決這個(gè)問(wèn)題呢?

網(wǎng)上的很多教程,有些是通過(guò) WebSocket 中間服務(wù)轉(zhuǎn)發(fā)器、網(wǎng)關(guān)轉(zhuǎn)發(fā)器 等實(shí)現(xiàn)方案,但這些實(shí)現(xiàn)方式有局限性,因?yàn)檫@些方案大部分是需要判斷用戶在哪臺(tái)服務(wù)器上(需要知道IP),然后轉(zhuǎn)發(fā)層將請(qǐng)求轉(zhuǎn)發(fā)到用戶所在服務(wù)器上。這種方案用戶單端登錄還好,如果用戶多端登錄 請(qǐng)求被轉(zhuǎn)發(fā)到多服務(wù)器上同時(shí)處理相關(guān)邏輯顯然是有問(wèn)題的,比如新增數(shù)據(jù)、修改數(shù)據(jù)…這些操作等,這種架構(gòu)解決方案 用戶多點(diǎn)平臺(tái)登錄時(shí)調(diào)整復(fù)雜度會(huì)變得較高。

將 Swoole Table (服務(wù)進(jìn)程間內(nèi)存共享) 改造為 Redis 哈希 來(lái)實(shí)現(xiàn)用戶與客戶端標(biāo)識(shí)(FD)綁定關(guān)系,主要目的是在單節(jié)點(diǎn)處理邏輯的時(shí)候經(jīng)常需要判斷對(duì)端用戶是否在線,單服務(wù)內(nèi)的共享內(nèi)存并不能知道其他服務(wù)內(nèi)該用戶是否在線,所以這個(gè)方案不可取了。改用 分布式緩存 就可以判斷出對(duì)端用戶是否在線了。

分布式緩存實(shí)現(xiàn)用戶與客戶端標(biāo)識(shí)(FD)綁定關(guān)系大致做法為:

  1. 在服務(wù)啟動(dòng)時(shí)創(chuàng)建一個(gè) 全局唯一ID,保證多服務(wù)下這個(gè) ID的唯一性,比如啟動(dòng)5個(gè)服務(wù)時(shí),每個(gè)服務(wù)的ID都不能有相同,目的是用來(lái)分布式緩存的客戶端FD標(biāo)識(shí)所在的服務(wù)ID,當(dāng)然 你也可以使用IP作為唯一性(可能會(huì)更直觀點(diǎn))。
  2. 將 唯一ID_FD 作為哈希鍵存儲(chǔ),在某個(gè)事件或定時(shí)清除不活躍的哈希鍵。要當(dāng)前某個(gè)服務(wù)的所有哈希鍵的時(shí)候可以使用 hScan 循環(huán)迭代模糊匹配實(shí)現(xiàn),必要時(shí)使用 hGetAll 獲取所有哈希鍵值(并發(fā)高服務(wù) 在此提醒謹(jǐn)慎使用哈)。

多節(jié)點(diǎn)服務(wù)器就會(huì)有分布式問(wèn)題,解決分布式問(wèn)題就找一個(gè)大家都能找到的地,比如說(shuō) MQTT、Kafka、RabbitMQ 等消息中間件,另外使用 Redis 的發(fā)布訂閱(pubsub)功能 也一樣可以實(shí)現(xiàn),不過(guò)在此我選擇的是用 RabbitMQ 來(lái)實(shí)現(xiàn)。

改進(jìn)后流程圖如下:

負(fù)載均衡(SLB) 內(nèi)所有服務(wù)啟動(dòng)時(shí)都綁定同一個(gè)RabbitMQ Fanout(廣播模式) 交換機(jī), 如果該交換機(jī)不存在則創(chuàng)建。然后每個(gè)服務(wù)都生成一個(gè)唯一的該交換機(jī)隊(duì)列(生成的交換機(jī)隊(duì)列不能相同, 比如可以服務(wù)器1生成的隊(duì)列名為 S1, 服務(wù)器2生成的隊(duì)列名為 S2), 可以將生成的隊(duì)列設(shè)置為 auto_delete: true, 這樣就可以達(dá)到當(dāng) 隊(duì)列沒(méi)有消費(fèi)者的時(shí)候該隊(duì)列會(huì)自動(dòng)刪除, 服務(wù)重啟時(shí)又重新生成的效果。接下來(lái)就是每個(gè)服務(wù)都注冊(cè)該交換機(jī)隊(duì)列的監(jiān)聽(tīng)消費(fèi),當(dāng)隊(duì)列的每一條息出棧時(shí)都會(huì)廣播到該交換機(jī)下的所有隊(duì)列(即所有服務(wù)的隊(duì)列監(jiān)聽(tīng)事件都能收到PUSH進(jìn)來(lái)的消息)。客戶端請(qǐng)求到 負(fù)載均衡(SLB) 任意一臺(tái)服務(wù)器該服務(wù)器邏輯處理完后將要發(fā)送給客戶端的消息推送至 RabbitMQ 消息隊(duì)列消息隊(duì)列將該消息廣播到所有服務(wù)器的監(jiān)聽(tīng)消費(fèi)事件內(nèi)所有服務(wù)器的監(jiān)聽(tīng)消費(fèi)事件內(nèi) Redis hScan 迭代遍歷當(dāng)前服務(wù)內(nèi)所有客戶端連接,取出所有符合用戶ID對(duì)應(yīng)的客戶端標(biāo)識(shí)(FD)進(jìn)行推送消息。(并發(fā)高時(shí)對(duì) Redis 沖擊很大,需要預(yù)估支撐力,對(duì)緩存哈希的讀要求隨并發(fā)高低而上升 O(n))

 

 

這種 WebSocket 分布式架構(gòu)解決方案同時(shí) 實(shí)現(xiàn)了支持單個(gè)用戶多設(shè)備、多平臺(tái)同時(shí)在線的場(chǎng)景,不需要知道有多少臺(tái)服務(wù)器(也就是說(shuō)服務(wù)器可以無(wú)限動(dòng)態(tài)擴(kuò)容),不需要知道用戶對(duì)應(yīng)哪些服務(wù)器,也不需要知道各個(gè)服務(wù)器的IP地址,只需要處理各自服務(wù)器內(nèi)的監(jiān)聽(tīng)消費(fèi)隊(duì)列即可相對(duì)于一些通過(guò)搭建轉(zhuǎn)發(fā)服務(wù)器、網(wǎng)關(guān)服務(wù)器等實(shí)現(xiàn)的 WebSocket 分布式架構(gòu) 有著天然的優(yōu)勢(shì),這些架構(gòu)解決方案要復(fù)雜很多,特別是要實(shí)現(xiàn)多設(shè)備、多平臺(tái)同時(shí)在線的場(chǎng)景時(shí) 更加、更加、更加復(fù)雜。

 

生活不易,如果您覺(jué)得這篇文章寫得不錯(cuò)就動(dòng)動(dòng)手指幫忙點(diǎn)個(gè)贊吧!感恩各位~

分享到:
標(biāo)簽:WebSocket
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定