1. 程式人生 > >WebIm解決方案:基於WebSocket+JavaWeb

WebIm解決方案:基於WebSocket+JavaWeb

有些需要了解的

首先呢,這是第一次寫,文筆不是很好,都是有一句說一句,但還是會盡可能的用通俗的話來寫; 其次呢,我也不是什麼高手,只是將自己的感悟寫出來,這個方案純屬是自己設想出來並加以實現的,不是技術教程也不是最好的解決方案,僅供參考;

有些需要掌握的

本解決方案需要有一定的JavaWeb基礎以及會常用的一些框架,最主要的是要懂核心WebSocket,裡面涉及到的技術不會詳細的講,我會盡可能的給相關的文章提供參考; 解決方案用到的技術:

  • 語言
    • Java(這個不用說都知道)
  • 框架(框架搭建教程
    • Spring Boot(搭建快)視訊教程
    • Mybatis(大家都用,上面框架搭建教程裡有教程)
  • 資料庫(資料庫安裝
    • mysql(同上)
  • 前端
    • bootstrap(懶得寫樣式)官網
    • angularJs(資料雙向繫結,方便)教程
  • 伺服器
    • Tomcat(簡單)
  • 通訊
    • WebSocket(如果要學的話,可以先做下面方案開始的第一步,然後基於這個來學WebSocket)WebSocket教程

以上用到的,其實都是比較簡單易學的,我這裡沒給出版本,根據自己的需要選擇吧;至於為什麼用,請看下面方案開始的第七步

方案開始

第一步、搭框架

根據上面所給出的先搭建出一個“健全”的web專案,當然可以用ssm或者ssh,甚至於不用框架普通web專案都行,最好用到maven,否則引包引到頭疼,不小心又說了一個東東,

啥是maven?

第二步、建個表

web專案弄好後,先建立幾張核心表(我這裡不應該用駝峰的),為什麼建這些表?又為什麼要用這些欄位?別急,慢慢看到第七步

  • 使用者表
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL COMMENT '使用者名稱',
  `userPwd` varchar(255) NOT NULL DEFAULT '' COMMENT '使用者密碼',
  `realname` varchar(255) DEFAULT '' COMMENT '真實姓名',
  `clientId` varchar(255) DEFAULT NULL COMMENT '客戶端ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8
  • 訊息記錄表(覺得欄位太多,自己精簡,不過這些欄位在這個方案都用得到)
CREATE TABLE `message_log` (
  `id` int(11) NOT NULL COMMENT '主鍵ID',
  `contextId` int(11) DEFAULT NULL COMMENT '上下文ID',
  `messageType` varchar(255) DEFAULT NULL COMMENT '訊息型別 0=普通文字,1=卡片,2=文章,3=音樂,4=紅包,5=圖片',
  `messageMode` varchar(255) DEFAULT NULL COMMENT '訊息模式 0=新增,1=撤回',
  `sendTime` int(11) DEFAULT NULL COMMENT '傳送時間',
  `message` varchar(255) DEFAULT NULL COMMENT '訊息內容(根據訊息型別格式化)',
  `isRead` int(11) DEFAULT NULL COMMENT '是否已讀',
  `fromId` varchar(255) DEFAULT NULL COMMENT '來源ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 訊息列表(聊天關係表)
CREATE TABLE `chat_user_relation` (
  `id` int(11) NOT NULL,
  `contextId` varchar(255) NOT NULL COMMENT '上下文ID',
  `userId1` int(11) DEFAULT NULL COMMENT '使用者ID,用於單聊使用,群聊為空',
  `userId2` int(11) DEFAULT NULL COMMENT '使用者ID2,用於單聊使用,群聊為空',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

第三步、增刪改查

將第二步建立的表,在專案中建立好對映關係,做好增刪改查(這個不難吧?);

第四步、前端介面

使用bootstrap+angularjs寫一個前端的頁面,簡單一點的話,就是一個輸入框,一個傳送按鈕,然後可以迴圈的列表(這裡可以用上面給出的websocket給出的教程中聊天室的例子的頁面),我個人建議寫得好看一點,花一兩天的時間搞一搞bootstrap(一兩天?不可能吧,抱歉,我就是一兩天就可以用得上手了,其實就是複製貼上),angularjs對於JavaScript不熟的童鞋來說,確實難了點,可以先用jQuery代替,有空再學;

第五步、前後端聯調

第三、四步完成後,前後調通,做到什麼地步?那就是賬號密碼登入後,A傳送訊息到後臺存庫,並且自己還能從庫中查到剛剛發的那條訊息返回給前端A,不懂?那我不說了,這裡只要求前後端能通;

第六步、上WebSocket

這裡就不說那麼多了,直接複製上面的websocket教程中的服務端程式碼,複製一份第四步的程式碼,寫一個websocket的通訊(覺得麻煩,這個可以省略?)

第七步、方案的核心

表設計說明:

  • 訊息記錄表(message_log) 說明

    • contextId 訊息記錄的上下文標識,與訊息列表的contextId關聯,這裡定義的是字元型別,所以可以自由發揮這個id的規則,最簡單的使用UUID;
    • messageType 訊息型別,這裡的訊息型別你可以認為是微信的那種,比如普通文字,公眾號文章,紅包,圖片,視訊等等這種型別,這個型別將會和下面的message緊緊關聯起來;
    • messageMode 其實主要就是做撤回的標記,其他可以是更新和刪除;
    • sendTime 傳送時間,由於客戶端和服務端會存在時間差,所以這裡這個時間有點雞肋,存在的原因很簡單,就是用於排序用的;
    • message 訊息內容,這裡有點複雜,我的實現方式是這裡存放json字串,是的沒錯,所以當java讀取這個欄位時,需要根據messageType來格式化對應的實體,便於操作,而前端js來說,這個就更簡單了,JSON.parse(message)就好了,另外前端可以做一個類似Android那樣的webview,也就是富文字的型別都用這個所謂的webview實現,其他的文字,圖片,視訊等,可以直接輸出(本來就是html);
    • isRead 是否已讀,這個的存在其實是替代訊息佇列的離線訊息,改成資料庫的已讀未讀;
    • fromId 這個在申請新增好友或者臨時會話的時候意義重大,重大到可以不用contextId;
  • 訊息列表(chat_user_relation)說明

    • contextId 打死都不說;
    • userId1 使用者的ID,當單聊的時候,從這個欄位可以直接找到使用者的資訊,反過來,使用者想查自己的訊息列表的時候,也可以根據這個查到contextId,只要有上下文ID,那你就可以飛了;
    • userId2 和上面的一樣
    • 延伸 這個中間表在使用者的角度來說,不存在,所以可以再做一個表和這個中間表關聯,當然也是和contextId關聯,那就是訊息盒子,換句話說就是對於使用者真正意義上的訊息列表,這個表可以存放名字(群聊的名字,或對方的名字)、頭像(對方的頭像)、上次傳送的內容、時間,不明白?看一下微信訊息列表image

通訊機制說明

在這個解決方案裡面,websocket不是webim的通訊機制,而是原來http,get、post這些,為什麼這麼說呢?有什麼好處? websocket確實可以實現即時通訊,開發成本低,但是有很多問題,其中一個問題,在做這個webim的時候,傳送一個富文字的資料就斷開了連結,而據說websocket能支援100萬字節,這應該和程式碼、業務有關係,並且我也沒有做websocket連結的維護等等。所以我選擇用websocket作為通知使用,也就是說,傳送訊息,獲取訊息,歷史記錄等等還是原來的配方,只是當我或他人傳送訊息時,會觸發websocket通知對方呼叫獲取訊息的介面拉取訊息,並將訊息置為已讀,這樣能很大程度減少websocket的使用;這麼做還有一個好處,就是檔案的傳送,因為還是用到了以前的上傳控制元件,上傳成功後依舊websocket通知一下對方拿就好了;所以看到這裡就知道我不是大神了吧,也就是個搬磚的,大神是會用到訊息伺服器,訊息佇列等等,我也研究了一個,百萬級分散式開源物聯網MQTT訊息伺服器

前端說明

bootstrap支援響應式佈局,當然響應式佈局的UI多了去了,可以用其他的; angularJs用這個是因為資料的雙向繫結很方便,在迴圈資料時,提供排序的功能,指定迴圈的集合中的某一個欄位進行排序,這個用在歷史記錄很好用,因為歷史記錄要做分頁,我們插入下一頁時是要向上插入的,但向上插入也還是要排序的,所以乾脆插入到原來的集合重新排序,然後判斷分頁的節點,用js重新定位到那裡就好了,有點像錨點; 訊息列表的動態排序用到這個排序功能也很好用,比如指定最後傳送時間作為訊息排序欄位倒序,已經排好序的訊息列表,當訊息來的時候,只需要修改當前這個會話的時間就會自動排好序,不需要自己去維護; 其次,當我們要擴充套件到通訊錄、朋友圈等等功能的時候,離開了訊息列表頁但還要接受訊息,這時候就要用到路由功能,並將websocket放置到全域性的controllerJs中;

其他要說的

方案到此也差不多了,這裡還可以做一個優化,就是將websocket抽出來做一個服務,因為只是用到了通知功能; 另外,做圖片或者檔案功能的時候,可以用MongoDB作為檔案資料庫,MongoDB是非關係型資料庫,存放檔案的資訊之後會的到一個ID,這樣在傳送圖片或者檔案的時候,只需要傳這個ID,然後再通過介面從MongoDB取就可以了,如圖片的src可以這麼寫src="http://localhost/file/getFileById=MongoDB中的ID"; 通訊錄的話可以再用一個表維護,偷懶可以直接用訊息列然後再排個序;