實時性
1.線程池多線程,把消息同步給其他端和對方用戶,其中數據持久化往往是最浪費時間的操作,可以使用mq異步存儲,因為其他業務不需要拿著整條數據,只需要這條數據的id進行操作。
2。消息校驗前置,放在tcp層(netty服務中)
可靠性
tcp協議只能保證數據在傳輸過程中不丟失,但不能保證到某一端不會丟失,比如傳輸過程中接收方斷網,數據就無法發送到接收方 。
可靠性可以通過消息重發來保證消息一定送到了對方手中。可以通過雙重ack確認機制來實現:
A向im服務端發送數據,服務端收到就返回一個ack,A收到這個ack就停止重發,否則就再次嘗試重發。當然重發需要設計上限次數的。
然后服務端接到A的數據,再發送給B,B接到之后告訴服務端,返回一個接受到的ack。服務端把收到的再傳回給A,此時A確定了此次傳輸是成功的。中間任何環節出問題,A就執行重發就ok了。
如果B不在線,服務端是可以感知到的,服務端可以代替B向A回復停止重發指令。
有序性
因為我們使用多線程保證了消息的實時性,那么就會導致消息有亂序的風險。
解決方案:
使用redis的incr命令實現原子性的遞增序列號,但是過度依賴redis可能會因為redis的崩潰而造成系統失效
冪等性
再im服務端存儲每次發過來的消息id(設置過期時間),重復發送的消息id將不再進行持久化但仍會給另一個客戶端發送消息。這樣就會導致另一個客戶端由于網絡問題如果沒有及時返回ack確認,那么他確實會收到2條相同的消息,但是在去重是完全可以處理的。
qq發消息失敗會有紅色感嘆號,當再次點擊重發時是把它當作一條新的消息id發送的,而不是之前的消息id。
消息已讀功能
在寫擴散中,每個人對自己的每條消息都可以直接獲取,已讀只不過是個字段罷了。
在讀擴散中,以群聊為例,可以在群成員表中加入一個字段,該字段表示改成員讀到的最后一條消息序列(保證有序性的那個遞增序列),在這條消息之前的就表示讀過了
離線消息存儲
IM(即時通訊)系統中的離線消息是指在目標用戶不在線或者不可達時,發送方發送的消息無法直接傳遞給目標用戶,而是被服務器暫時存儲起來,等到目標用戶上線或者可達時再進行投遞。
離線消息的存在是為了保證消息的可靠性和完整性。當發送方發送消息時,如果目標用戶在線,消息可以直接傳遞給目標用戶;但如果目標用戶不在線,服務器會將該消息存儲在消息隊列或者數據庫中,等到目標用戶上線后,服務器會將離線消息投遞給目標用戶。
離線消息通常具有以下特點:
- 持久化存儲:離線消息通常被存儲在服務器的數據庫或者消息隊列中,確保消息的持久性,即使服務器重啟或者斷電,消息也不會丟失。
- 時效性:離線消息通常會設置一個過期時間,在一定時間范圍內等待目標用戶上線,過期后會被清理或者丟棄。
- 投遞策略:服務器會在目標用戶上線后,根據一定的投遞策略(如先進先出、按時間戳等)將離線消息按順序投遞給目標用戶。
- 通知機制:當目標用戶上線后,服務器可能會發送通知給目標用戶,告知其有離線消息待接收。
這里的存儲使用redis,每人只存1000條,超過就淘汰最早的,使用zset存儲,使用消息的遞增序列號作為排序標準,zset支持查詢范圍內指定數量的元素(SMEMBERS命令)。
即使你錯過了離線消息的通知和消息盒子,你仍然可以通過滾動查看聊天記錄,找到之前的離線消息。
登錄之后的數據同步(歷史記錄的拉取)
不可能說每次登錄都把所有記錄都刪了重新拉取一遍的,所以這里采用增量拉取。
需要拉取的東西有會話,好友列表,好友申請,為每個需要拉取的數據記錄下他的遞增序列號,每次只拉取大于記錄里最大的序列號。
客戶端可以用數據庫sqllite
在線狀態設計
正常情況下,你的上線和下線等狀態應該通知給你的所有好友,和你在的所有群里的所有成員,這將是非常恐怖的數據量。
改進1:只推給在線用戶
改進2:
- 按需拉取:在按需拉取的策略下,IM 系統不會主動向所有好友和群成員發送上線和下線等狀態通知。相反,當其他用戶需要獲取某個用戶的狀態時,他們可以向服務器發送請求,然后服務器根據請求返回相應的狀態信息。這種方式可以避免將狀態通知廣播給所有人,只有真正需要獲取狀態信息的用戶才會發起請求,減少了不必要的數據傳輸和處理。
- 臨時訂閱:臨時訂閱是指用戶可以臨時訂閱某個好友或群組的狀態更新通知。當用戶訂閱了某個用戶或群組后,只有在被訂閱對象的狀態發生變化時,系統才會向訂閱者發送通知。這樣可以避免向所有好友和群成員廣播狀態更新,只有被訂閱的對象狀態發生變化時,才會發送通知給訂閱者。這種方式可以根據用戶的實際需求,選擇性地接收特定用戶或群組的狀態更新通知,減少了不必要的通知量。
陌生人發消息限制
-
計數限制:為每個用戶設置一個計數器,記錄他們發送給陌生人的消息數量。當陌生人發送消息時,系統會檢查計數器的值。如果計數器小于等于三,允許發送消息并將計數器加一;如果計數器大于三,拒絕發送消息。
-
時間限制:除了計數限制,可以設置一個時間限制,例如每小時或每天只允許陌生人發送三條消息。系統會記錄陌生人發送消息的時間,并在規定的時間段內檢查發送數量。如果超過限制,拒絕發送消息。