這個前端+后端+OpenAI的項目,就該這么寫簡歷!
分布式IM即時通訊系統(tǒng)本質(zhì)上就是對線上聊天和用戶的管理,針對聊天本身來說,最核心的需求就是:發(fā)送文字、表情、圖片、文件、語音、視頻、消息緩存、消息存儲、消息未讀、已讀、撤回,離線消息、歷史消息、單聊、群聊,多端同步,對接OpenAI大模型,以及其他一些需求。
對用戶管理來說,存在的需求包含:添加好友、查看還有列表、刪除好友、查看好友信息、創(chuàng)建群聊、加入群聊、查看群成員信息、@群成員、退出群聊、修改群昵稱、拉人進群、踢人出群、解散群聊、填寫群公告、修改群備注以及其他用戶相關的需求等。
注:拿小本子記錄下,后續(xù)可以寫到簡歷上的整合了OpenAI大模型的分布式IM即時通訊系統(tǒng),從此,簡歷上又多了一個可以拿的出手的高并發(fā)、高性能、高可用、可監(jiān)控、可預警、可伸縮,支持無限擴展的真實業(yè)務場景項目。
一、前言
為了能夠讓小伙伴們更好的理解分布式IM即時通訊系統(tǒng)的設計,我們站在架構師的角度,在充分了解系統(tǒng)需求,業(yè)務流程和技術流程后,從全局視角為系統(tǒng)設定方案目標,對技術方案進行選型,對系統(tǒng)進行總體架構設計和分層架構設計,并梳理清楚發(fā)送消息的交互鏈路、單聊和群聊的交互鏈路。以方便各位小伙伴將分布式IM即時通訊系統(tǒng)寫到自己的簡歷中,增強自己的競爭力。
二、方案目標
在進行技術選型與總體架構設計之前,需要明確一個事項,就是系統(tǒng)無論采用哪種方案,采用哪種架構設計都需要明確這種方案的業(yè)務目標、技術目標和架構目標,并在研發(fā)過程中不斷評估系統(tǒng)的總體性能表現(xiàn),發(fā)現(xiàn)系統(tǒng)瓶頸并不斷進行優(yōu)化。
總體上,我們搭建和開發(fā)的分布式IM即時通訊系統(tǒng),需要滿足如下方案目標。
- 業(yè)務目標:滿足需求設計篇章中的各類需求場景。
- 技術目標:支持無限擴容,百萬用戶同時在線聊天。
- 架構目標:高并發(fā)、高性能、高可用、可監(jiān)控、可預警、可伸縮,支持無限擴展。
三、技術選型
在技術選型上,除了采用SpringBoot等基礎框架外,也會采用容器化方案。同時,考慮到為了盡量降低技術門檻,在整個分布式IM即時通訊系統(tǒng)的技術選型中,主要采用市面上比較流行的技術框架和方案,具體選型如下所示。
- 開發(fā)框架:SpringBoot、SpringCloud、SpringCloud Alibaba、Dubbo。
- 緩存:Redis分布式緩存+Guava本地緩存。
- 數(shù)據(jù)庫:MySQL、TiDB、HBase。
- 流量網(wǎng)關:OpenResty+Lua。
- 業(yè)務網(wǎng)關:SpringCloud Gateway + Sentinel。
- 持久層框架:MyBatis、Mybatis-Plus。
- 服務配置、服務注冊與發(fā)現(xiàn):Nacos。
- 消息中間件:RocketMQ。
- 網(wǎng)絡通信:Netty。
- 文件存儲:Minio。
- 日志可視化治理:ELK。
- 容器化管理:Swarm、Portainer。
- 監(jiān)控:Prometheus、Grafana。
- 前端:Vue。
- 單元測試:Junit。
- 基準測試:JMH。
- 壓力測試:JMeter。
四、系統(tǒng)初步架構設計
對于IM即時通訊系統(tǒng)來說,涵蓋了即時通訊后端服務、大后端平臺、SDK接入服務、OpenAI接入服務、大前端UI,我相信不少小伙伴多多少少能夠畫出IM即時通訊系統(tǒng)的架構圖,大致如圖1-1所示。
圖片
其實,這種這種架構設計也比較常見,在這種架構設計中,Kong/Openresty/Nginx只做負載均衡和反向代理,研發(fā)人員更多的是關業(yè)務層和基礎層的開發(fā),流量比較小時,這種架構設計一般不會有什么問題。
但是一旦流量比較大,用戶調(diào)用后端平臺的接口發(fā)送消息時,即時通訊SDK同步調(diào)用即時通訊服務的接口就會出現(xiàn)性能問題。
因為每個終端同時只能與一個IM即時通訊服務實例建立連接,如果大量的用戶終端恰好都與一個IM即時通訊服務建立連接,那即時通訊SDK頻繁同步調(diào)用同一個IM即時通訊服務的接口就會出現(xiàn)性能瓶頸。
此時,出現(xiàn)性能瓶頸時,不僅僅會影響到IM即時通訊服務,也會對后端平臺接收請求的業(yè)務造成一定的影響。
五、系統(tǒng)架構設計優(yōu)化
既然圖1-1所示的架構設計存在性能瓶頸,那我們?nèi)绾芜M行優(yōu)化呢?為此我們在如1-1的基礎上進行了優(yōu)化,優(yōu)化后的架構如圖1-2所示。
圖片
對比圖1-1和圖1-2可以看出,在屏蔽掉技術實現(xiàn)細節(jié)的前提下,我們將對業(yè)務的校驗和流量管控進行前置化,放大Kong/OpenResty/Nginx的職責,使得這些軟件不僅具備反向代理和負載均衡的功能,還能實現(xiàn)限流、黑白名單、流量管控、業(yè)務校驗等功能。
也就是說,在這種架構模式下,我們充分發(fā)揮了整個分布式IM即時通訊系統(tǒng)的入口職責,充分利用Kong/OpenResty/Nginx的高并發(fā)、高吞吐量的能力,盡量將大部分無效請求擋在整個系統(tǒng)之外。例如,用戶在沒登錄系統(tǒng)的前提下,就嘗試調(diào)用發(fā)送消息、添加好友、添加群組等等接口。這樣會大大減輕后臺平臺的業(yè)務壓力。
除了在Kong/OpenResty/Nginx中實現(xiàn)限流、黑白名單、流量管控、業(yè)務校驗等功能外,我們還引入了業(yè)務網(wǎng)關集群,實現(xiàn)限流、降級、熔斷、流控、校驗、鑒權等功能,進一步保證下游系統(tǒng)的穩(wěn)定性和安全。
為了解決大量用戶終端恰好連接到同一個IM即時通訊服務實例,IM即時通訊SDK頻繁調(diào)用同一個IM即時通訊服務實例的接口造成的性能問題。我們在IM即時通訊服務SDK與IM即時通訊服務之間引入了RocketMQ集群。
IM即時通訊服務集群中的每一個IM即時通訊服務實例在集群中都有一個唯一的ID,并且每個IM即時通訊服務實例在啟動后,只會監(jiān)聽RocketMQ中與自身ID相關的Topic。這樣每個IM即時通訊服務只會收到與自身ID相關的Topic中的消息,不會接收所有的消息。
當用戶登錄系統(tǒng)后,就會與IM即時通訊服務建立長連接,并且會以用戶ID和終端為Key,以IM即時通訊服務的ID為value,將其存儲到分布式緩存中。同時,會以用戶ID和終端為Key,以用戶終端與IM即時通訊服務建立的長連接為value,將其存儲到IM即時通訊服務本地內(nèi)存中。
當用戶調(diào)用后端平臺的接口發(fā)消息時,會帶上目標用戶的ID,并且在IM即時通訊SDK中會指定用戶登錄的終端設備,最終會通過IM即時通訊SDK向RocketMQ發(fā)送消息,此時IM即時通訊SDK會根據(jù)目標用戶ID和終端從分布式緩存中獲取目標用戶連接的IM即時通訊服務的ID,并向此ID相關的Topic發(fā)送消息。
此時與目標用戶建立長連接的IM即時通訊服務就會接收到RocketMQ中的消息,隨后根據(jù)用戶ID和終端從本地緩存中獲取到與用戶終端建立的長連接,并基于此長連接向用戶推送消息。
那么問題來了:這種架構設計還有進一步優(yōu)化的空間嗎?
六、容器化架構設計
為進一步增強分布式IM即時通訊系統(tǒng)的性能、可用性和彈性伸縮能力,我們可以對分布式IM即時通訊系統(tǒng)進行容器化架構設計,如圖1-3所示。
圖片
可以看到,我們對分布式IM即時通訊系統(tǒng)的架構設計進行了進一步優(yōu)化,采用了容器化架構設計。在原有架構的基礎上,我們進行了如下改進和優(yōu)化。
(1)基礎支撐服務
基礎支撐服務會由各種基礎中間件、數(shù)據(jù)存儲服務、以及監(jiān)控服務實現(xiàn),包含:MySQL數(shù)據(jù)庫、TiDB數(shù)據(jù)庫、HBase、Redis緩存、RocketMQ消息隊列、Prometheus監(jiān)控和Portainer容器管理等基礎中間件實現(xiàn),基礎支撐服務會對整個分布式IM即時通訊系統(tǒng)提供最基礎的數(shù)據(jù)、傳輸、監(jiān)控和容器管理等服務。
(2)容器化
在容器化層面,會通過Docker、Swarm和Portainer實現(xiàn),其中,會基于Swarm和Portainer對容器化進行管理。
(3)其他基礎性功能實現(xiàn)
除了上述分層架構外,對于建設分布式IM即時通訊系統(tǒng)來說,還要考慮異常監(jiān)控、服務注冊與發(fā)現(xiàn)、可視化、服務降級與兜底數(shù)據(jù)、服務限流、服務容災、容量規(guī)劃與擴縮容和全鏈路壓測等。
七、DDD分層業(yè)務架構設計
在分布式IM即時通訊系統(tǒng)中,不管是大后端平臺,還是IM即時通訊服務,我們都會對業(yè)務層的代碼采用分層業(yè)務架構,這里,可以借鑒DDD的分層架構思想。
將代碼總體上分成展示層、應用層、領域?qū)雍突A設施層四個層次,但是,考慮到分布式IM即時通訊系統(tǒng)的特殊性,又不會嚴格按照DDD的原則來設計代碼分層,具體按照如圖1-4所示。
圖片
可以看到,分布式IM即時通訊系統(tǒng)會借鑒DDD的設計思想,但是不會完全按照DDD的方式進行設計。
(1)展示層
展示層,也叫做用戶UI層,是DDD設計的最上層,對外提供API接口,接收客戶端請求,解析參數(shù),返回結果數(shù)據(jù),并對異常進行處理。
(2)應用層
應用層,也叫做Application層,應用層主要處理容易變化的業(yè)務場景,可對相關的事件、調(diào)度和其他聚合操作進行相關的處理。
(3)領域?qū)?/h4>
領域?qū)?,也叫做Domain層,領域?qū)涌梢哉f是DDD設計的精髓所在,它是將業(yè)務系統(tǒng)中相對不變的部分抽象出來封裝成領域模型。
在分布式IM即時通訊系統(tǒng)的設計中,領域?qū)踊静粫蕾嚻渌麑?,也不會依賴基礎設施層,這里是與DDD設計存在區(qū)別的地方。
(4)基礎設施層
基礎設施層,也叫做Infrastructure層,基礎設施層會對其他各層提供通用的基礎能力,在分布式IM即時通訊系統(tǒng)中,就包括了緩存、通用工具類、消息、系統(tǒng)的持久化機制等。
八、發(fā)送消息交互鏈路
在分布式IM即時通訊系統(tǒng)中,我們忽略掉其他一些細節(jié)信息,重點關注下發(fā)送消息的交互鏈路邏輯。不管是單聊還是群聊,最終都需要通過IM即時通訊服務將消息推送給用戶的終端。此時發(fā)送消息的流程如圖1-5所示。
圖片
可以看到,用戶在分布式IM即時通訊系統(tǒng)發(fā)送消息時,不管是單聊還是群聊,最終的消息都會推送到用戶登錄的終端設備上。假設此時用戶A給用戶B發(fā)送消息,或者用戶A和用戶B在同一個群組,用戶A向群組發(fā)送消息,用戶B接收消息的主要流程如下。
(1)用戶A調(diào)用后端平臺的接口向用戶B發(fā)送消息,并且發(fā)送的消息中會帶有用戶B的ID以及終端信息。
(2)后端平臺將消息緩存起來,并且會將消息異步寫入消息庫。
(3)后端平臺從Redis中獲取用戶B連接的IM即時通訊服務的ID。
(4)后端平臺獲取到用戶B連接的IM即時通訊服務的ID后,會向RocketMQ中用戶B連接的IM即時通訊服務ID對應的Topic發(fā)送消息。
(5)IM即時通訊服務會監(jiān)聽自身服務ID對應的RocketMQ中Topic的消息,此時,用戶B連接的IM即時通訊服務會接收到消息。
(6)IM即時通訊服務接收到消息后,會根據(jù)用戶B的ID以及終端信息從緩存中獲取用戶B與IM即時通訊服務建立的連接,并且通過這個連接向用戶B推送消息。
要實現(xiàn)如上發(fā)送消息的流程,前提是要滿足如下條件。
(1)后端平臺滿足分布式條件,可隨時橫向擴展。
(2)IM即時通訊服務滿足分布式條件,可隨時橫向擴展。
(3)每個啟動的IM即時通訊服務實例在集群中都有一個唯一的ID。
(4)每個IM即時通訊服務,都只監(jiān)聽自身ID對應的RocketMQ中Topic的消息。
(4)用戶登錄分布式IM即時通訊系統(tǒng)后,會與IM即時通訊服務建立長連接,并且會根據(jù)用戶ID和所在的終端緩存長連接,同時會根據(jù)用戶ID和所在的終端將連接的IM即時通訊服務的ID緩存到Redis。
(6)用戶發(fā)送消息時,會根據(jù)目標用戶的ID和終端從Redis中獲取IM即時通訊服務的ID,進而向當前IM即時通訊服務的ID對應的RocketMQ的Topic發(fā)送消息。
(7)對應的IM即時通訊服務監(jiān)聽并接收到RocketMQ消息后,會根據(jù)目標用戶的ID和終端從緩存中獲取到用戶的連接信息,向目標用戶推送消息。
九、單聊交互鏈路
單聊就是在分布式IM即時通訊系統(tǒng)中,一個用戶直接與另外一個用戶聊天,也就是一對一的聊天。在這種場景下,很有可能單聊的兩個用戶中,出現(xiàn)用戶不在線的情況。
例如,用戶A給用戶B發(fā)送消息時,用戶B可能不在線。此時,我們就需要將用戶A向用戶B發(fā)送的消息存儲起來。其實,在我們實現(xiàn)的分布式IM即時通訊系統(tǒng)中,無論把用戶B是否在線,都會存儲消息記錄。當用戶B登錄系統(tǒng)后,將消息同步給用戶B,如圖1-6所示。
圖片
可以看到,用戶A向用戶B發(fā)送消息時,如果用戶B在線,就可以按照發(fā)送消息的交互鏈路向用戶B發(fā)送消息了。如果用戶B不在線,此時就無法向用戶B正常推送消息。
當用戶B登錄分布式IM即時通訊系統(tǒng)后,就會調(diào)用后端平臺的接口拉取所有未讀消息,并通過用戶B在線流程向用戶B推送消息。
十、群聊交互鏈路
群聊就是在分布式IM即時通訊系統(tǒng)中,多個用戶在同一個群組中進行聊天,此時在發(fā)送消息時,我們可以通過群組ID找出群內(nèi)所有在線的用戶,將消息即時發(fā)送給在線的用戶。那些未在線的用戶就按照單聊未在線的用戶進行處理,如圖1-7所示。
圖片
可以看到,群聊的交互鏈路流程如下所示。
(1)用戶調(diào)用后端平臺的接口向群組發(fā)送消息。
(2)后端平臺將消息緩存并異步寫入消息庫。
(3)由于是向群組發(fā)送消息,群里有多個用戶,此時就會從Redis中獲取所有用戶連接的IM即時通訊服務ID列表。
(4)對用戶按照服務ID分組,將相同服務ID下的用戶分在同一個邏輯分組里,方便后續(xù)推送消息,并且會記錄未在線的用戶列表。
(5)循環(huán)向每個服務ID對應的RocketMQ中的Topic發(fā)送消息。
(6)廣播處理未在線用戶的未讀消息ID。
(7)IM即時通訊服務會監(jiān)聽自身服務ID對應的Topic,會隨時接收推送到自身服務的消息。
(8)當IM即時通訊服務接收到消息后,此時用戶掉線,或者用戶不在線,向用戶推送消息就會失敗,或者未查詢到用戶與IM即時通訊服務建立的連接,就不會向用戶推送消息。
(9)當用戶登錄分布式IM即時通訊系統(tǒng)后,會從后端平臺拉取歷史(離線)消息,并通過用戶在線的流程,向用戶推送消息。
好了,看到這里,你明白如何設計一個高度可擴展的分布式IM即時通訊系統(tǒng)了嗎?趕緊拿本子記錄下你學到的知識,將其整理到簡歷上吧!
十一、如何寫簡歷描述
大部分簡歷上都會有項目描述部分,也就是要求寫你所經(jīng)歷的項目,對于某個具體的項目來說,一般可以從項目描述、所使用的技術以及你在項目中的職責三個方面進行介紹。