一、目錄介紹
功能梳理
具體實現
二、需求梳理
透過前面兩章內容的學習,我們基本學會了如何使用 Netty 建立一個長連線,接下來我們就在這個基礎上,實現一個單機版的 im 系統。
主要功能,我梳理了一下:
登入
維持連線、心跳檢測
聊天訊息
訊息ack
使用到的相關元件:
SpringBoot-job
GuavaCache
三、具體實現
本期的內容是基於,原理篇一的 dome 程式碼基礎上進行的,沒有看過的原理一的小夥伴,建議先回顧一下原理篇一。
原理篇一的程式碼結構:
Server(主程式)
ServerHandler(業務處理程式)
實戰篇一的程式碼結構:
程式碼的層級結構如上所示,接下來,我們將會一個個模組對邏輯進行講解。
1、登入
1)實現邏輯
不管是長連線還是短連線,鑑權這個動作都是要有的,我相信這個功能模組,大家是很好理解的。我這裡就不在過多的贅述了,具體實現步驟如下所示:
1、前後端建立 ws 連線
2、前端傳送登入型別的報文,如下所示:
{ “token”: “2”, “type”: “10”}
token:這裡的 token,就是使用者登入標識,大家可以根據自己所依賴的業務系統,進行修改。
type:這裡表示訊息報文的型別,本文所有型別定義如下所示:
USER_LOGIN
(10, “使用者上線”)
USER_LOGIN_RESP
(11, “使用者上線響應”)
HEARTBEAT_TIMEOUT
(30, “心跳超時”)
PING
(40, “心跳”)
PONG
(41, “心跳響應”)
CHAT
(80, “聊天”),
CHAT_RESP
(81, “聊天響應”)
ACK
(90, “確認”)
ACK_RESP
(91, “確認響應”)
UNKNOWN
(0, “未知型別”)
示例程式碼如下圖所示,WsMsgDispatcher。dispatch
訊息型別
3、後端對 token 進行校驗,校驗成功就記錄使用者登入資訊。
示例程式碼如下圖所示,
UserLoginProcessor。login
登入業務邏輯
2)具體效果
主要的業務程式碼我們已經講解完畢了,接下來我們來看看效果:
使用者登入
從上圖,我們可以看到,我們登入的兩個使用者都成功了,並且返回了對應的使用者資訊。
2、維持連線、心跳檢測
這個模組的功能,其實我們
在原理篇二的時候已經講過了具體的實現方案
了,這裡也不再過多的贅述了,我們直接來看具體實現方法吧。
1)維持連線
1、前端每10秒傳送一次心跳訊息,報文如下所示:
{ “type”: “40”, “fromId”: “2”}
注:前端傳送的每個訊息,理論上都是需要帶上使用者表示的,後端都是需要進行鑑權操作的。我們這裡為了方便講解(偷懶,bushi)將這部分邏輯進行了簡化,大家在具體實現的時候,記得一定要加上
鑑權邏輯
。
2、後端檢測,使用者是否還線上,如果線上,則重新整理使用者的最新線上時間,並回復 PONG 訊息。
示例程式碼如下圖所示,
HeartBeatProcessor。process
維持連線1
維持連線2
2)心跳檢測
這裡主要是基於
IdleStateEvent
事件實現的。
TextWebSocketFrameHandler
繼承
SimpleChannelInboundHandler
類,並實現
userEventTriggered
方法,具體程式碼如下所示:
心跳檢測
這裡詳細說一下,三種事件的區別:
readerIdleTimeSeconds:
讀超時。即當在指定的時間間隔內沒有從
Channel
讀取到資料時,會觸發一個
READER_IDLE
的
IdleStateEvent
事件。
writerIdleTimeSeconds:
寫超時。即當在指定的時間間隔內沒有資料寫入到
Channel
時,會觸發一個
WRITER_IDLE
的
IdleStateEvent
事件。
allIdleTimeSeconds:
讀/寫超時。即當在指定的時間間隔內沒有讀且沒有寫操作時,會觸發一個
ALL_IDLE
的
IdleStateEvent
事件。
所以,我們這裡檢測 ALL_IDLE 事件即可。
3)具體效果
維持連線效果如下所示:
維持連線效果
心跳檢測效果如下所示:
心跳超時效果1
心跳超時效果2
3、聊天訊息
聊天訊息模組主要分為兩部分:
訊息接收:客戶端推送訊息到服務端
訊息推送:服務端將訊息推送到指定的客戶端
這邊主要的難點在於,服務端將訊息推送到指定的客戶端,具體場景有2種情況:
訊息的傳送者和訊息的接受者,在同一臺伺服器上建立的 ws 連線,這種情況,就很好處理,直接在伺服器上找到建立的 ws 連線,然後將訊息推送給對應的客戶端。
訊息的傳送者和訊息的接受者,在不同的伺服器上建立的 ws 連線,這種情況就比較複雜,實現方案也很多,比較簡單的實現方式就是,傳送一條廣播訊息,讓對應的伺服器,將訊息推送到指定的客戶端。
本文由於是
單機版
的 im,所以只會有第一種情況發生,第二種情況就留給大家自由發揮了。
1)訊息接收
具體步驟如下所示:
1、客戶端傳送型別為80的報文,如下所示:
{ “type”: “80”, “fromId”: “1”, “toId”: “2”, “content”: { “contentType”: 1, “body”: “測試訊息” }}
2、服務端(ChatProcessor)對訊息進行處理,具體程式碼如下所示:
訊息接收
2)訊息推送
具體步驟如下所示:
1、獲取訊息接受者所連線的伺服器 ip 地址 2、判斷當前伺服器 ip 地址是否和上面的 ip 地址相同,如果相同則推送訊息,否則轉發給目標伺服器
具體程式碼如下所示:
訊息推送
3)具體效果
1、我們先登入兩個使用者,分別是張三、李四,如下圖所示:
聊天登入
2、張三傳送訊息給李四,如下圖所示:
張三傳送訊息給李四
3、李四傳送訊息給張三,如下圖所示:
李四傳送訊息給張三
4、訊息 ack
因為網路環境異常或者其他異常狀況的傳送,可能會出現訊息推送失敗的情況,這時候就需要 訊息 ack 機制和重試,來保證我們的訊息可以推送成功。
1)訊息 ack 機制
具體步驟如下:
1、客戶端收到 80 型別的訊息,解析併發送 ack 報文,如下所示:
{ “type”: “90”, “msgId”: “2bfea133-72a8-4315-82aa-80049fe4fb7b”}
2、服務端收到 ack 訊息,變更訊息狀態(AckProcessor),具體程式碼如下圖所示:
訊息ack
2)訊息重試
這裡因為是單機版 im,所以直接採用 SpringBoot-Job 實現,Job 程式碼如下所示:
訊息重試
以上文章來源於Java知音 ,作者啊傑