介紹
預計3篇分享:這次是第一篇,專案的整體介紹和實體關係的梳理
可以註冊客戶端賬號,也可以使用初始預設賬號,現有初始賬號說明:
賬號 | 密碼 | 說明 |
---|---|---|
admin | 123456 | 管理端賬號 |
user | 123456 | 客戶端普通使用者賬號 |
muteuser | 123456 | 客戶端被禁言使用者賬號 |
disabled | 123456 | 客戶端被封禁使用者賬號 |
member1 | 123456 | 客戶端普通使用者賬號 |
member2 | 123456 | 客戶端普通使用者賬號 |
... | 123456 | 客戶端普通使用者賬號 |
member30 | 123456 | 客戶端普通使用者賬號 |
<img width="300" src="https://i.loli.net/2020/07/15/fO2naUmPluYRsBd.png">
功能簡介
- 註冊,登入,個人、群組聊天,個人資訊編輯等基礎功能
- 申請新增好友和申請入群
- 表情,圖片,視訊,定位資訊支援
- 聊天會話列表記錄
- 訊息記錄(微信的訊息記錄真實一言難盡)
- 支援多點同時登入
- 百度 UNIT 機器人自動回覆(todo)
- 管理端,進行角色和許可權的管理,群狀態管理(我也當一回馬化騰)
需求簡介
移動網際網路發展至今,以微信為首的即時通訊服務已經融入了我們生活中的各個角落,在公司的一些業務中也扮演著重要的角色,對於即時通訊我們公司原來是使用的環信的服務,但是有很多定製化的需求無法實現,所以後來決定內部開發一個滿足定製化需求的即時通訊微服務。
使用socket.io
框架是因為當時後端缺人,加上看了一些例子後覺得使用起來真的很方便,而且全平臺支援,所以這個微服務就在前端團隊進行落地實踐,目前效果還不錯。
社群目前這方面的內容比較少或者太簡陋(只有一個公共的聊天室這種)。另外就是在業務開發過程中被 PM 搞得很難受,所以想脫離一些特有的業務上的東西,實現一個功能簡單五臟俱全的不摻雜公司業務的 IM 應用,包含服務端,管理端和客戶端。客戶端的模仿物件是微信,因為我很熟悉,不用在產品上面思考太多,另外就是試用的人很熟悉,不需要太多的溝通成本。
框架簡介
要開發一套完整的即時通訊服務,需要以下部分:
- 服務端:用來實現基礎的服務介面和資料持久化
- 客戶端:完成登入、聊天等基礎功能,類似微信
- 管理端:管理群組、使用者和角色許可權
server
為企業級框架和應用而生
選用阿里的 egg.js 框架做支撐,看中的原因是他們內部大規模的落地和安全方面做得比較好,沒有選擇 nest 的原因是整合 socket.io
比較麻煩,ORM 選用 sequelize,資料庫是 mysql ,之前一起使用過,上手難度小
admin
開箱即用的中臺前端/設計解決方案
選擇 Ant Design Pro 作為模板開發管理端,選用的原因是我對 Vue 全家桶比較熟悉,想借著這個機會熟悉下整套 React 生態 的開發流程,感受下目前國內兩大開發框架的本質區別和殊途同歸,Ant Design Pro 已經發布了好幾年了,也的確給中小型企業帶來效率的提升,也正好適合我這的需求。
client
?️ Vue.js 開發的標準工具
使用 @vue/cli 搭建 IM 服務的客戶端,一個移動端的 H5 專案,UI 框架使用的有贊 vant,整合了我的開源元件vue-page-stack和黃老師的better-scroll,實現 IM 的基礎功能
實體關係
作為一個前端工程師,大多數的日常工作是不需要思考實體關係的。但是,就我的實際體驗來看,懂得實體關係可以幫助我們更好的理解業務模型。而對產品和業務理解的提升對我們的幫助是非常大的,可以在需求評審的時候發現很多不符合邏輯的地方(怎麼又要吐槽產品經理了),這時候能提出來就會主動避免我們在後續的過程中進行反覆開發,同時可以和產品側的同學形成比較良好的互動(而不是互懟)。下面簡單羅列下比較重要的實體關係:
<img width="600" src="https://i.loli.net/2020/07/14/Zhz85V2ptOylDcj.png">
通過上圖可以看到 user 是整個關係圖中的核心,下面介紹下各個實體之間的關係:
- user 和 user_info(使用者資訊) 是一對一的關係
- user 和 role(角色)是多對多的關係
- role 和 right(許可權)是多對多的關係
- user 和 apply(申請)是多對多的關係,申請都是涉及到兩個 user(申請人和被申請人)
- user 和 group(群組)是多對多的關係
- group 和 conversation(會話) 是一對一的關係
- friend 和 conversation(會話) 是一對一的關係
- conversation 和 message(訊息)是 1 對多的關係
- friend(好友關係) 和 user 沒有直接關係,friend 由兩個 user 確定
下面詳細介紹下會話、角色與許可權:
會話
完成一個即時通訊應用,需要考慮的第一個事情就是會話,就是我們微信裡面的對話視窗。思考會話和訊息、使用者、群組之間的關係花費了不少的精力,最終形成以下的基本關係:
- 2 個使用者參與的聊天屬於建立了 Friend 關係(互為好友)
- 多個使用者參與的聊天組成了群組關係
- Friend 和會話之間的關係是 1 對 1 的關係,可以通過 Friend 找到此 Friend 的會話,也可以通過會話確定 Friend
- 群組和會話之間的關係是 1 對 1 的關係,可以通過群組找到此群組的會話,也可以通過會話確定群組
- 訊息屬於某個會話,可以根據會話檢視對應的訊息列表
- 儲存訊息的時候更新會話的啟用時間,使用者的會話列表根據啟用時間排序,也就是最近的會話再最前面
也就是說,使用者和會話沒有直接的關係,只能通過使用者對應的單聊和群聊去獲取會話,這樣做可以有以下的好處:
- 無論是單聊還是群聊,連線上的使用者只要 join 進對應的會話 room 裡面就可以,訊息也是在對應的 room 裡面釋出
- 無論是單聊還是群聊,訊息的儲存和查詢都比較簡單,都是隻針對這個會話
- 獲取個人的會話列表也變得很簡單,使用者的會話列表通過查詢使用者『所有的 Friend 和群組』->『所有的會話』->『排序會話(根據啟用時間)』,就可以獲取
角色和許可權
為了設計一個靈活、通用、方便的許可權管理系統,本系統採用 RBAC(基於角色的訪問控制)控制,來設計一個通用的『使用者角色許可權』平臺,方便後期擴充套件。
RBAC
RBAC(基於角色的訪問控制)是指使用者通過角色與許可權進行關聯。即一個使用者擁有若干角色,每一個角色擁有若干許可權(當然了,別把衝突的角色和許可權配在一起)。這樣,就構造成“使用者—角色—許可權”的授權模型。在這種模型中,使用者與角色之間、角色與許可權之間,一般是多對多的關係。
本系統預設的角色和許可權
本系統預設有管理員、一般使用者、禁言使用者和封禁使用者這幾種角色,給不同的角色分配不同的許可權,所以需要針對管理和發言等介面路由做一下統一的鑑權(通過中介軟體的方式)處理,具體方式和方法在後端專案中會詳細說明。本系統暫時採用預先定義了角色和許可權的方式,後續想要擴充套件的話可以編輯角色和許可權。
管理員
沒見過微信的管理端,但是可以想象一下,管理員可以配置使用者的角色和許可權,可以編輯群組的狀態:
- 登入的許可權
- 群組狀態的編輯
- 針對使用者的角色和許可權的編輯
普通使用者
註冊登入後,可以正常的新增好友和加入群組,可以修改個人基礎資訊和處理申請
- 註冊登入
- 編輯個人基礎資訊
- 新增好友,申請入群
- 處理好友申請和入群申請
- 聊天
禁言使用者
- 註冊登入
- 編輯個人基礎資訊
- 新增好友,申請入群
- 處理好友申請和入群申請
封禁使用者
無法登入
角色的組合
舉個例子:現在有一個新版的個人中心需要上線測試,首先新建一個角色『測試個人中心』,再給這個角色分配對應的許可權;然後給普通使用者做個分組,選出一些人配置上這個角色,這樣就可以進行測試了。
即時通訊原理
下面說下即時通訊服務的核心通訊原理,和一般的 http 服務一樣,有一個服務端和客戶端進行通訊,只不過詳細的協議和處理方式不一樣。
WebSocket
由於歷史原因,現在主流的 http 協議是無狀態協議(HTTP2 暫時應用不廣泛),一般情況是由客戶端主動發起請求,然後服務端去響應。那麼為了實現服務端向客戶端推送資訊,就需要前端主動向後端去輪詢,這種方式低效且容易出錯,在之前我們的管理端首頁確實是這麼做的(5s 一次)。
為了實現這種服務端主動推送資訊的需求, HTML5 開始提供一種在單個 TCP 連線上進行全雙工通訊的協議,也就是 WebSocket。WebSocket 使得客戶端和伺服器之間的資料交換變得更加簡單,允許服務端主動向客戶端推送資料。WebSocket 協議在 2008 年誕生,2011 年成為國際標準,目前絕大部分瀏覽器都已經支援了。
WebSocket 的用法相當簡單:
var ws = new WebSocket("wss://echo.websocket.org");
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
有了 WebSocket 協議讓服務端主動推送資訊有了先進的武器,那麼有沒有什麼方式可以相容新舊瀏覽器呢?其實很多人想到了這點,答案就是socket.io
socket.io
socket.io
進一步封裝了WebSocket
的介面,而且可以在舊版本瀏覽器中自主切換到使用輪詢的方式進行通訊(我們使用者是不會感知的),形成了一套統一的介面,大大減輕了開發的負擔。主要具有以下優點:
- 封裝出了一套非常易用的介面,前後端統一,使用非常簡單
- 全平臺支援(原生和 H5,微信小程式中也有對應的實現)
- 自適應瀏覽器,在比較老的瀏覽器中主動切換使用輪詢的方式,不需要我們自己搞輪詢
最快,最可靠的即時通訊引擎(FEATURING THE FASTEST AND MOST RELIABLE REAL-TIME ENGINE)
使用起來真的很簡單:
var io = require('socket.io')(80);
var cfg = require('./config.json');
var tw = require('node-tweet-stream')(cfg);
tw.track('socket.io');
tw.track('javascript');
tw.on('tweet', function(tweet){
io.emit('tweet', tweet);
});
總結
有了以上的基礎,我們就基本完成了開始寫程式碼之前的準備:明確了這個應用的基礎功能和實體之間的關係,也明確了基礎的技術方案選型,劃分了3個專案的各自任務。下面就開始完成 server 端吧
未完待續:下一篇介紹 server 端