掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
大家好,我是小,一個(gè)漂泊江湖多年的 985 非科班程序員,曾混跡于國(guó)企、互聯(lián)網(wǎng)大廠和創(chuàng)業(yè)公司的后臺(tái)開(kāi)發(fā)攻城獅。

成都創(chuàng)新互聯(lián)公司是一家專(zhuān)注于做網(wǎng)站、網(wǎng)站設(shè)計(jì)與策劃設(shè)計(jì),太白網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)10年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:太白等地區(qū)。太白做網(wǎng)站價(jià)格咨詢:13518219792
前些天所在部門(mén)出去團(tuán)建,于是公司行政和 HR 拉了一個(gè)微信群,發(fā)布一些跟團(tuán)和集合信息。
當(dāng)我正在查看途徑路線和團(tuán)建行程時(shí),忽然一條帶著喜意的消息撲面而來(lái),消息上赫然帶著八個(gè)大字:恭喜發(fā)財(cái),大吉大利。
圖片
搶紅包??!原來(lái)是公司領(lǐng)導(dǎo)在群里發(fā)了個(gè)紅包,于是引得群?jiǎn)T哄搶?zhuān)瑲夥掌錁?lè)融融。
畢竟,團(tuán)不團(tuán)建無(wú)所謂,不上班就很快樂(lè);搶多搶少無(wú)所謂,有錢(qián)進(jìn)就很開(kāi)心。
打工人果然是最容易滿足的生物!手動(dòng)狗頭 >
我看著群里嬉戲打鬧的聊天,心中陷入了沉思:微信這個(gè)集齊了陌生人聊天、文件分享和搶紅包功能的群聊設(shè)計(jì)確實(shí)有點(diǎn)意思,如果在面試或者工作中讓我們?cè)O(shè)計(jì)一個(gè)群聊系統(tǒng),需要從哪些方面來(lái)考慮呢?
面試官:微信作為 10 億用戶級(jí)別的全民 App,有用過(guò)吧?
我:(內(nèi)心 OS,說(shuō)沒(méi)用過(guò)你也不會(huì)相信啊~)當(dāng)然,親愛(ài)的面試官,我經(jīng)常使用微信來(lái)接收工作消息和文件,并且經(jīng)常在上面處理工作內(nèi)容。
面試官:(內(nèi)心 OS:這小伙子工作意識(shí)很強(qiáng)嘛,加分!)OK,微信的群聊功能是微信里面核心的一個(gè)能力,它可以將數(shù)百個(gè)好友或陌生人放進(jìn)一個(gè)群空間,如果讓你設(shè)計(jì)一個(gè)用戶量為 10 億用戶的群聊系統(tǒng),你會(huì)怎么設(shè)計(jì)呢?
我:首先群聊功能是社交應(yīng)用的核心能力之一,它允許用戶創(chuàng)建自己的社交圈子,與家人、朋友或共同興趣愛(ài)好者進(jìn)行友好地交流。
以下是群聊系統(tǒng)常見(jiàn)的幾個(gè)功能:
圖片
除了功能需要,當(dāng)我們面對(duì) 10 億微信用戶每天都可能使用建群功能的情景時(shí),還需要處理大規(guī)模的用戶并發(fā)。
這就引出了系統(tǒng)的非功能需求,包括:
面試官:嗯,不錯(cuò),那你可以簡(jiǎn)要概述一下這幾個(gè)常用的功能嗎?
我:好的,我們首先做系統(tǒng)的概要設(shè)計(jì),這里涉及到群聊系統(tǒng)的核心組件和基本業(yè)務(wù)的概要說(shuō)明。
群聊系統(tǒng)中,會(huì)涉及到如下核心組件和協(xié)議。
圖片
在業(yè)務(wù)概要說(shuō)明里,我們關(guān)注用戶的交互方式和數(shù)據(jù)存儲(chǔ)......
面試官:稍等一下,群聊系統(tǒng)的好友建群功能比較簡(jiǎn)單,拉好友列表存數(shù)據(jù)就可以了!你用過(guò)面對(duì)面建群吧,可以簡(jiǎn)要說(shuō)一下如何設(shè)計(jì)面對(duì)面建群功能嗎?
我:(內(nèi)心 OS,還好之前在吃飯時(shí)用過(guò)面對(duì)面建群結(jié)賬,不然就G了),好的,群聊系統(tǒng)除了拉好友建群外,還支持面對(duì)面建群的能力。
用戶發(fā)起面對(duì)面建群后,系統(tǒng)支持輸入一個(gè) 4 位數(shù)的隨機(jī)碼,周?chē)挠脩糨斎胪粋€(gè)隨機(jī)碼便可加入同一個(gè)群聊,面對(duì)面建群功能通常涉及數(shù)據(jù)表設(shè)計(jì)和核心業(yè)務(wù)交互流程如下。
圖片
用戶 A 在手機(jī)端應(yīng)用中發(fā)起面對(duì)面建群,并輸入一個(gè)隨機(jī)碼,校驗(yàn)通過(guò)后,等待周?chē)?0 米之內(nèi))的用戶加入。此時(shí),系統(tǒng)將用戶信息以 HashMap 的方式存入緩存中,并設(shè)置過(guò)期時(shí)間為 3min。
{隨機(jī)碼,用戶列表[用戶A(ID、名稱(chēng)、頭像)]}用戶 B 在另一個(gè)手機(jī)端發(fā)起面對(duì)面建群,輸入指定的隨機(jī)碼,如果該用戶周?chē)羞@樣的隨機(jī)碼,則進(jìn)入同一個(gè)群聊等待頁(yè)面,并可以看到其它群?jiǎn)T的頭像和昵稱(chēng)信息。
此時(shí),系統(tǒng)除了根據(jù)隨機(jī)碼獲取所有用戶信息,也會(huì)實(shí)時(shí)更新緩存里的用戶信息。
圖片
當(dāng)?shù)谝粋€(gè)用戶點(diǎn)擊進(jìn)入該群時(shí),就可以加入群聊,系統(tǒng)將生成的隨機(jī)碼保存在 RandomCode 表中,并關(guān)聯(lián)到新創(chuàng)建的群 ID,更新群成員的個(gè)數(shù)。
然后,系統(tǒng)將用戶信息和新生成的群聊信息存儲(chǔ)在 Group、GroupMember 表中,并實(shí)時(shí)更新群成員個(gè)數(shù)。
然后,B 用戶帶著隨機(jī)碼加入群聊時(shí),手機(jī)客戶端向服務(wù)器后端發(fā)送請(qǐng)求,驗(yàn)證隨機(jī)碼是否有效。后臺(tái)服務(wù)檢查隨機(jī)碼是否存在于緩存中,如果存在,則校驗(yàn)通過(guò)。
然后,根據(jù) Group 中的成員個(gè)數(shù),來(lái)判斷當(dāng)前群成員是否滿員(目前普通用戶創(chuàng)建的群聊人數(shù)最多為 500 人)。
如果驗(yàn)證通過(guò),后臺(tái)將用戶 B 添加到群成員表 GroupMember 中,并返回成功響應(yīng)。
面試官:如果有多個(gè)用戶同時(shí)加入,MySQL 數(shù)據(jù)庫(kù)如何保證群成員不會(huì)超過(guò)最大值呢?
我:有兩種方式可以解決。一個(gè)是通過(guò) MySQL 的事務(wù),將獲取 Group 群成員數(shù)和插入 GroupMember 表操作放在同一個(gè)事務(wù)里,但是這樣可能帶來(lái)鎖表的問(wèn)題,性能較差。
另一種方式是采用 Redis 的原子性命令incr 來(lái)記錄群聊的個(gè)數(shù),其中 key 為群聊ID,value 為當(dāng)前群成員個(gè)數(shù)。
當(dāng)新增群?jiǎn)T時(shí),首先將該群聊的人數(shù)通過(guò) incr 命令加一,然后獲取群成員個(gè)數(shù)。如果群?jiǎn)T個(gè)數(shù)大于最大值,則減一后返回群成員已滿的提示。
使用 Redis 的好處是可以快速響應(yīng),并且可以利用 Redis 的原子特性避免并發(fā)問(wèn)題,在電商系統(tǒng)中也常常使用類(lèi)似的策略來(lái)防止超賣(mài)問(wèn)題。
同時(shí),在面對(duì)面建群的過(guò)程中相當(dāng)重要的能力是標(biāo)識(shí)用戶的區(qū)域,比如 50 米以內(nèi)。這個(gè)可以用到 Redis 的 GeoHash 算法,來(lái)獲取一個(gè)范圍內(nèi)的所有用戶信息。
由于篇幅有限,這里不展開(kāi)贅述,想了解更多位置算法相關(guān)的細(xì)節(jié),可以看我之前的文章:聽(tīng)說(shuō)你會(huì)架構(gòu)設(shè)計(jì)?來(lái),弄一個(gè)公交&地鐵乘車(chē)系統(tǒng)。
面試官:嗯不錯(cuò),那你再講一下群聊系統(tǒng)里的消息發(fā)送和接收吧!
我:當(dāng)某個(gè)成員在微信群里發(fā)言,系統(tǒng)需要處理消息的分發(fā)、通知其他成員、以及確保消息的顯示。
在群聊系統(tǒng)中保存和展示用戶的圖片、視頻或音頻數(shù)據(jù)時(shí),通常需要將元數(shù)據(jù)和文件分開(kāi)存儲(chǔ)。
其中元數(shù)據(jù)存儲(chǔ)在 MySQL 集群,文件數(shù)據(jù)存儲(chǔ)在分布式對(duì)象存儲(chǔ)集群中。
消息發(fā)送和接收的時(shí)序圖如下所示:
圖片
除了上述建群功能中提到的用戶表和群組表以外,存儲(chǔ)元數(shù)據(jù)還需要以下表結(jié)構(gòu):
面試官:我們時(shí)??吹饺毫挠?n 個(gè)未讀消息,這個(gè)是怎么設(shè)計(jì)的呢?
我:MessageState 表記錄了用戶的未讀消息數(shù),想要獲取用戶的消息未讀數(shù)時(shí),只需要客戶端調(diào)用一下接口查詢即可獲取,這個(gè)接口將每個(gè)群的未讀個(gè)數(shù)加起來(lái),統(tǒng)一返回給客戶端,然后借助手機(jī)的 SDK 推送功能加載到用戶手機(jī)上。
面試官:就這么簡(jiǎn)單嗎,可以優(yōu)化一下不?
我:(內(nèi)心 OS,性能確實(shí)很差,就等著你問(wèn)呢)是的,我們需要優(yōu)化一下,首先 MySQL 查詢 select count 類(lèi)型的語(yǔ)句時(shí),都會(huì)觸發(fā)全表掃描,所以每次加載消息未讀數(shù)都很慢。
為了查詢性能考慮,我們可以將用戶的消息數(shù)量存入 Redis,并實(shí)時(shí)記錄一個(gè)未讀數(shù)值。并且,當(dāng)未讀數(shù)大于 99 時(shí),就將未讀數(shù)值置為 100 且不再增加。
當(dāng)推送用戶消息時(shí),只要未讀數(shù)為 100,就將推送消息數(shù)設(shè)置為 99+,以此來(lái)提升存儲(chǔ)的性能和交互的效率。
面試官:嗯,目前幾乎所有的消息推送功能都是這么設(shè)計(jì)的。那你再說(shuō)一下 10 億用戶的群聊系統(tǒng)應(yīng)該如何在高并發(fā),海量數(shù)據(jù)下保證高性能和高可用吧!
我:我想到了幾個(gè)點(diǎn),比如采用集群部署、消息隊(duì)列、多線程、緩存等。
在群聊系統(tǒng)中,我們用到了分布式可擴(kuò)展的思想,無(wú)論是長(zhǎng)連接服務(wù)、消息推送服務(wù),還是數(shù)據(jù)庫(kù)以及分布式文件存儲(chǔ)服務(wù),都是集群部署。
一方面防止單體故障,另一方面可以根據(jù)業(yè)務(wù)來(lái)進(jìn)行彈性伸縮,提升了系統(tǒng)的高可用性。
在消息推送時(shí),由于消息量和用戶量很多,所以我們將消息放到消息隊(duì)列(比如 Kafka)中異步進(jìn)行消費(fèi)和推送,來(lái)進(jìn)行流量削峰,防止數(shù)據(jù)太多將服務(wù)打崩。
在消息寫(xiě)入和消費(fèi)時(shí),可以多線程操作,一方面節(jié)省了硬件開(kāi)銷(xiāo),不至于部署太多機(jī)器。另一方面提升了效率,畢竟多個(gè)流水線工作肯定比單打獨(dú)斗更快。
緩存前面已經(jīng)說(shuō)到了,除了建群時(shí)記錄 code,加群時(shí)記錄群成員數(shù),我們還可以緩存群聊里最近一段時(shí)間的消息,防止每個(gè)用戶都去 DB 拉取一遍數(shù)據(jù),這提升了消息查閱的效率。
除此之外,為了節(jié)省成本,可以記錄流量的高峰時(shí)間段,根據(jù)時(shí)間段來(lái)定時(shí)擴(kuò)縮節(jié)點(diǎn)(當(dāng)然,這只是為了成本考慮,在實(shí)際業(yè)務(wù)中這點(diǎn)開(kāi)銷(xiāo)不算什么大問(wèn)題)。
面試官:嗯不錯(cuò),實(shí)際上的架構(gòu)中也沒(méi)有節(jié)省這些資源,而是把重心放在了用戶體驗(yàn)上。(看了看表)OK,那今天的面試就到這,你有什么想問(wèn)的嗎?
我:(內(nèi)心 OS,有點(diǎn)慌,但是不能表現(xiàn)出來(lái))由于時(shí)間有限,之前對(duì)系統(tǒng)高并發(fā)、高性能的設(shè)計(jì),以及對(duì)海量數(shù)據(jù)的處理淺嘗輒止,這在系統(tǒng)設(shè)計(jì)的面試中占比如何?
面試官:整體想得比較全,但是還不夠細(xì)節(jié)。當(dāng)然,也可能是時(shí)間不充分的原因,已經(jīng)還不錯(cuò)了!
我:(內(nèi)心 OS,借你吉言)再想問(wèn)一下,如果我把這些寫(xiě)出來(lái),會(huì)有讀者給我點(diǎn)贊、分享、加入在看嗎?
面試官:……
群聊系統(tǒng)是社交應(yīng)用的核心功能之一,每個(gè)社交產(chǎn)品幾乎都有著群聊系統(tǒng)的身影:包括但不限于 QQ、微信、抖音、小紅書(shū)等。
上述介紹的技術(shù)細(xì)節(jié)可能只是群聊系統(tǒng)的冰山一角,像常見(jiàn)的搶紅包、群內(nèi)音視頻通話這些核心功能也充斥著大量的技術(shù)難點(diǎn)。
但正是有了這些功能,才讓我們使用的 App 變得更加有趣。而這,可能也是技術(shù)和架構(gòu)的魅力所在吧~

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流