一、背景
隨著建立通道的房間和人數(shù)增加到一定規(guī)模,給我們的架構(gòu)之?dāng)U展性提出了考驗(yàn),本文主要是講述通道服務(wù)的水平擴(kuò)展性,垂直擴(kuò)展不在本文的范圍內(nèi)。
前文也有講述,我們的現(xiàn)狀是缺乏通道的接入層,連接是隨機(jī)路由到后端節(jié)點(diǎn)。換句話說(shuō),同一個(gè)房間的用戶,會(huì)分散到多個(gè)后端節(jié)點(diǎn)。這樣的話,A用戶想要發(fā)送消息給同房間的B用戶,目前是使用Mq廣播消息,讓所有的后端節(jié)點(diǎn)都消費(fèi)該消費(fèi)。
缺點(diǎn)是:很多消息廣播出去,導(dǎo)致消息的重復(fù)處理,每個(gè)節(jié)點(diǎn)都要消費(fèi)同等數(shù)量的消息。最重要的是,這不利于服務(wù)的水平擴(kuò)展。
二、目標(biāo)
- 1、彈性擴(kuò)展
- 2、通道的連接路由到后端節(jié)點(diǎn),有效期默認(rèn)為當(dāng)天,允許隔天重新創(chuàng)建通道
- 3、擴(kuò)展的同時(shí)不能隨意更換房間
- 4、擴(kuò)展的同時(shí)不能斷開已連上的通道
三、方案一--kong plugin改寫upstream
自定義kong插件,調(diào)用方額外傳入header字段,計(jì)算hash值,選擇對(duì)應(yīng)的upsteam。

既然是upstream,就意味著后端的pod節(jié)點(diǎn)可以是多個(gè),對(duì)比方案二的高可用相對(duì)更好。
主要內(nèi)容是開發(fā)自定義的kong插件,讀取websocket header頭部字段,求模計(jì)算,選擇對(duì)應(yīng)的upstream。
好處是:可以記錄下各個(gè)upstream已連接上的課堂列表,當(dāng)有新的課堂來(lái)臨,可以選擇路由到連接空閑的upstream。
四、方案二--kong upsteam 的 hash on header
要求調(diào)用方的頭部,新增上傳計(jì)算路由的字段。比如學(xué)校ID,用戶ID,課堂ID等等。

不用額外的開發(fā)工作,相對(duì)比方案一,它的路由機(jī)制沒(méi)有那么靈活,只保證同一個(gè)課堂下的連接請(qǐng)求到同一個(gè)節(jié)點(diǎn)。而無(wú)法對(duì)節(jié)點(diǎn)的負(fù)載進(jìn)行適當(dāng)?shù)木鶖偅热绻?jié)點(diǎn)1已連接了2個(gè)課堂,節(jié)點(diǎn)2還未有1個(gè)課堂,來(lái)臨的第3個(gè)課堂還有可能連接到第1個(gè)節(jié)點(diǎn)。
五、方案三--命名服務(wù)之zookeeper
單元化的需要,我們根據(jù)不同的學(xué)校ID可配置不同的通道地址。本次需求可以進(jìn)一步細(xì)化到課堂ID,在每次進(jìn)入課堂前,調(diào)用命名服務(wù),取得配置好的通道地址。

客戶端增加一次獲取通道地址的請(qǐng)求,可以在創(chuàng)建課堂的時(shí)候,就確定其應(yīng)該連接的通道地址。
但是業(yè)務(wù)服務(wù)要獲取通道地址,改動(dòng)的代碼就比較多了。最主要的是業(yè)務(wù)服務(wù)不可能每次都獲取,要求把<課堂ID,通道地址>存儲(chǔ)到Jvm內(nèi)存。
pod在創(chuàng)建的時(shí)候,注冊(cè)到zk,隨著節(jié)點(diǎn)銷毀而從zk下線。這使得通道集群保持最新。
六、方案四--命名服務(wù)之gossip
上面額外引入了zookeeper這一中間件,你可以使用分布式協(xié)議gossip,使得通道集群的信息在各個(gè)節(jié)點(diǎn)之間共享。

主要步驟如下:
- 1、客戶端向通道服務(wù)發(fā)起http請(qǐng)求,頭部攜帶課堂ID。
- 2、服務(wù)端任意一個(gè)節(jié)點(diǎn),根據(jù)課堂ID的hash算法,得出它應(yīng)該連接的通道地址,返回給客戶端。
- 3、客戶端根據(jù)上一步得到的通道地址,建立ws連接。
- 4、業(yè)務(wù)服務(wù)集群的每個(gè)jvm節(jié)點(diǎn),在啟動(dòng)的時(shí)候,任意連上一個(gè)通道地址。由該鏈路,實(shí)時(shí)推送課堂和通道的映射關(guān)系給業(yè)務(wù)服務(wù)。
- 5、業(yè)務(wù)服務(wù)得到N個(gè)連接地址,除自身已連上的通道地址外, 以次和剩下的通道建立連接。
改動(dòng)點(diǎn):
- 1、客戶端在建立連接前,多一次獲取通道地址的請(qǐng)求,可以是ws協(xié)議,也可以是http協(xié)議。
- 2、業(yè)務(wù)服務(wù)如果有2個(gè)節(jié)點(diǎn),通道服務(wù)有4個(gè)節(jié)點(diǎn),那么每個(gè)節(jié)點(diǎn)會(huì)建立4個(gè)通道連接,總共會(huì)有4 * 2 = 8 個(gè)連接。

上面獲取通道地址是通過(guò)http協(xié)議,你也可以使用ws協(xié)議。流程如下:

這里,把kong網(wǎng)關(guān)變成了"SLB總?cè)肟?,它會(huì)可能二次建立連接,好處是通道服務(wù)不用額外提供http接口了。
七、問(wèn)題場(chǎng)景
1、場(chǎng)景一--ws連接數(shù)不夠
- 解決方法:擴(kuò)展后端服務(wù)的節(jié)點(diǎn)數(shù),也即擴(kuò)容pod。
2、場(chǎng)景二--N個(gè)課堂的使用隔離性
- 讓通道服務(wù)變成有狀態(tài)的,rabbitmq廣播消息通過(guò)vhost隔離多個(gè)后端集群。
- 不同的課堂路由到不同的后端集群,甚至可以針對(duì)一個(gè)大課堂對(duì)應(yīng)一個(gè)后端節(jié)點(diǎn)。
3、場(chǎng)景三--保證通道列表的變更實(shí)時(shí)刷新
- 業(yè)務(wù)服務(wù)和通道服務(wù)保持長(zhǎng)連接,由通道實(shí)時(shí)推送給業(yè)務(wù)服務(wù)。
4、場(chǎng)景四--保證路由關(guān)系的變更實(shí)時(shí)刷新
- 通道服務(wù),在客戶端和通道服務(wù)建立連接前,我們根據(jù)客戶端上報(bào)上來(lái)的頭部字段,計(jì)算出應(yīng)該路由到哪個(gè)通道。
- 第二步,找到業(yè)務(wù)服務(wù)和自己連接上的通道,實(shí)時(shí)推送給業(yè)務(wù)服務(wù)。由后者進(jìn)行刷新內(nèi)存,供選擇通道。
八、總結(jié)
數(shù)據(jù)存儲(chǔ)
- 課堂對(duì)應(yīng)的通道地址的映射關(guān)系(每次都會(huì)根據(jù)課堂ID,計(jì)算出它應(yīng)該對(duì)應(yīng)哪個(gè)通道)
- 通道列表的刷新(涉及到通道服務(wù)的彈性擴(kuò)縮容)
消息發(fā)送的兩種方式
- 1、客戶端和通道服務(wù)建立連接
- 2、客戶端調(diào)用業(yè)務(wù)服務(wù)的接口,由業(yè)務(wù)服務(wù)告知通道服務(wù)。(這里的業(yè)務(wù)服務(wù)充當(dāng)中轉(zhuǎn)的角色)
