【軟件架構】IM架構設計(安卓版)

1. 架構總覽

Alt 架構縱覽

2. 模塊介紹

2.1 協議封裝與任務流程

2.1.1 協議與任務的封裝

Alt

  1. 協議有協議頭(協議頭由於格式相同,被抽象出來)和協議體組成,協議有兩類:請求協議(request)和回覆協議(response);
  2. 任務(action)由請求協議、回覆協議和任務回調(callback)組成;
  3. callback是針對客戶端主動請求協議的相應處理,分別是成功回調、超時回調和失敗回調;

2.1.2 消息(任務)流程

Alt

  1. 由UI或SYSTEM觸發一個消息的生成,隨之將其投遞到發送隊列中,等待發送;
  2. 消息發送線程會不停的從發送隊列中拉取一個消息併發送出去,同時放入超時監測隊列;而當網絡斷開時會等待若干時間並從新循環,若檢測該消息在隊列等待時間過長,則丟棄該消息並觸發相應的失敗回調;
  3. 對於客戶端主動請求,在收到服務端給予回覆時,調用該消息的成功回調處理相應回覆;
  4. 對於超時線程監測超時的消息,移除超時監測隊列,並調用超時回調。

2.2 定時任務

    定時任務的實現主要由TimerHelper類與ITimerProcessor兩個類,第一個類主要實現了Timer的功能,ITimerProcessor爲一個接口,當要創建一個Timer的時候,須要新創建一個類實現ITimerProcessor中得process接口來處理具體的業務。緩存

    TimerHelper類的內部封裝了系統的Timer及TimerTask來實現,對外提供startTimer以及stopTimer接口。startTimer須要一個布爾類型的參數標誌啓動定時任務是須要執行一次,仍是永久執行。
    新生成TimerHelper實例的時候須要制定定時任務的時間以及定時觸發處理接口ITimerProcessor的具體實現。服務器

    固然在Timer的設計中,並無徹底採用系統提供的Timer類實現,有些Timer是採用線程模擬實現,這個主要是基於Java 中Timer的一些缺陷來考慮。網絡

    下面簡單介紹下四種Timer架構

2.2.1 心跳Timer

    心跳Timer 是維持客戶端與服務端長連一個強有力的保證。網絡中接收發送都是使用socket的recv與send進行發送與接收,若是此套接字已經斷開,則發送與接收數據都會出現問題,建立心跳機制,就是爲了及時檢測該套接字是否有效。併發

    所謂心跳就是給服務端發送一個自定義的包,來告訴服務端,本身在線,以確保長連的有效性。異步

2.2.2 發送超時Timer

    在開發網絡應用程序的時候,處理業務和通信流程之間常常會出現矛盾。這種矛盾主要是因爲二者之間的不一樣步形成的。socket

    好比,網絡的延遲較大,而實際業務處理的速度則相對比較快,那麼若是處理完某一事務而後等待發送成功再處理下一個事務則會大大下降效率。因此創建了一個發送消息隊列。這是一個典型的「生產者消費者」模型,業務邏輯將須要發送的數據放到消息隊列中,SendPacketMonitor從消息隊列中取出數據,併發送出去。ide

    因爲使用了消息隊列的模式,發送就變成了一種異步操做,業務邏輯將消息放入消息隊列後,就能夠進行其餘的操做,而沒法知道該消息是否真正發送成功。所以,在設計消息隊列的時候採用了回調機制,業務方在放入消息隊列的時候,必須實現onSuccess接口與onTimeOut接口,分別在發送成功與發送超時調用。函數

    發送超時Timer是採用線程來模擬實現的,在SendPacketMonitor類,做爲消費者,會不停的從消息隊列中取出數據,取出數據後,會判斷該消息產生的時刻與當前時間相比較,若是發現時差已經超過系統定義的最大超時時間,則直接調用「生產者」的onTimeOut接口,通知其發送超時。spa

2.2.3 重連Timer

    重連是另外一個保證長連的機制,雖然使用了心跳機制來保證長連,可是因爲網絡環境的複雜性,沒法保證在一個鏈接開啓後,就永遠保持鏈接,所以,重連就成了另一個保證。

    重連主要是爲了當鏈接斷開的時候,客戶端可以自動快速的鏈接到服務端。爲了系統的穩定性,及相應快速,重連Timer採用的是線程模擬Timer實現。

重連的邏輯中,會去檢測服務端的心跳包,若是發現長時間沒有收到服務端的任何數據包,則認爲該socket已經失效,並進行重連。

    在重連Timer中,爲了防止雪崩效應的出現,在檢測到socket失效,並非立馬進行重連,而是讓客戶端隨機Sleep一段時間再去鏈接服務端,這樣就可使不一樣的客戶端在服務端重啓的時候不會同時去鏈接,從而形成雪崩效應。

2.2.4 好友狀態Timer

    雖然在實現的邏輯中,服務端在好友狀態變化的時候,會主動推送消息給客戶端,可是仍是設計了好友狀態Timer。由於在網絡複雜的環境中,有太多的未知因素。

    好友狀態Timer的基本實現就是每隔一段時間發送一個數據包請求得到好友的狀態信息。當收到響應數據包的時候,就會去更新Cache中的好友狀態信息。


2.3狀態管理

2.3.1 狀態管理StateManager

    狀態管理就是一個多狀態的狀態機,其中包括Net狀態管理(指硬件網絡是否可用),Socket狀態管理(指與MsgServer的鏈接是否可用)。

    狀態管理主要功能就是採集Net狀態與Socket狀態,提供兩個接口notifyNetState與notifySocketState兩個接口供Net狀態管理與Socket狀態管理調用,當接收到狀態變化的時候會調用NetDispach進行網絡狀態變動分發。

2.3.2 Net狀態管理NetStateManager

    Net狀態管理指的是物理網絡狀態的管理,主要管理當前物理網絡是否可用以及進行變化時進行監聽,當監聽到網絡斷開或者連上事件的時候,會調用狀態管理notifyNetState接口。

    在應用啓動的時候咱們會註冊網絡變化廣播接收

    public class ConnectionChangeReceiver extends BroadcastReceiver

    Override他的onReceive函數,在onReceive 函數中獲取網絡鏈接服務,而後調用NetStateManager的setState接口,通知狀態變化。

2.3.3 Socket狀態管理

    Socket狀態管理指的是客戶端與MsgServer之間的鏈接狀態。當檢測到鏈接不可用時,會調用該接口的setState接口設置狀態。當socket的channelConnected、exceptionCaught與channelDisconnected函數被調用的時候以及在重連出現異常或失敗的時候會通知進行狀態變動。

2.3.4 狀態變動分發

    狀態變動分發對外提供三個接口register,unregister與dispachMsg接口。外界若是關心網絡狀態變化事件,能夠註冊本身的Handler到該類,當網絡狀態發生變化的時候,會根據註冊的Handler進行事件通知。

    register接口爲提供註冊的接口。

    unregister接口爲取消註冊的忌口。

    dispachMsg爲事件分發接口,當網絡狀態發生變化的時候,該接口會被調用,通知各個Handler進行處理。


2.4 斷線重連

    斷線重連機制是當IM與MsgServer斷開後可以自動鏈接。

    斷線重連爲一個單獨的線程,進行循環:

    當檢測到NetState爲不可用的時候,會隨機睡眠1-9秒而後繼續檢測。同時會檢測心跳包,當發現最後一次收到心跳包超過MAX_HEART_BEAT_TIME時間會認爲Socket鏈接不可用而重置SocketState的狀態。

    當檢測到網絡可用,Socket狀態不可用的時候,就會啓動斷線重連機制,在進行斷線重連以前,會進行鏈接次數判斷,若是爲第一次重連,則隨機睡眠1秒多,這個機制主要是爲了防止服務端出現異常而重啓的時候大量的客戶端同時鏈接上來而發生雪崩現象。若是不爲第一次重連,則睡眠指定時間,該時間的計算公式以下:

  1. nSleep =(long)Math.pow(2, mnReconnectCount);
    if(nSleep >16){
        nSleep =16;
    }

    該計算公式主要是爲了防止大量的客戶端不停的進行重連從而對服務端形成大量的壓力,另外從節省客戶端的能耗考慮。

    每次進行重連都會將重連次數累加,這個主要是爲了防止之後須要對重連次數進行限制。
    重連過程以下圖:

Alt

2.5 登錄

    登錄流程主要分爲如下幾個流程:

    一、認證;二、獲取MsgServer地址;三、登錄MsgServer。

    下面依次介紹。

2.5.1 認證

    認證過程主要是對用戶合法身份的驗證,包括以下兩個方面:

    從主客獲取AppToken。該過程是一個反射調用,IM程序調用主客的獲取Token接口獲取到主客的AppToken,Dao等信息。

    拿從主客獲取到得AppToken及Dao信息到IM的驗證服務器去換取一個IMToken,該過程爲一個Http調用。服務器會對上傳的AppToken及Dao信息進行校驗,若是校驗成功,則會返回一個IMToken,以及LoginServer地址等信息,後期須要拿該Token到MsgServer進行登錄驗證。

    認證的過程主要在TokenManager中實現,該類中還對Token時效進行了管理,當獲取IMToken的時候會先對判斷IMToken是否爲空,若是爲空則去獲取AppToken等信息,再去服務端換取IMToken,不然判斷Token的時效是否失效效,若是失效則獲取AppToken並換取IMToken信息,若是有效,則直接返回IMToken。

2.5.2 獲取MsgServer地址

    該過程是客戶端經過驗證時拿到的LoginServer地址創建Socket鏈接,併發送獲取MsgServer請求,LoginServer會返回一個可用的MsgServer的Ip及Port。

2.5.3 登錄MsgServer

    當通過2.5.2獲取到MsgServer的IP及Port後,會根據給定的IP與Port與MsgServer創建一個Socket鏈接,當鏈接創建成功後。會攜帶獲取到得IMToken,用戶名等信息發送一個登錄請求包,若是登錄請求驗證經過,客戶端啓動一個Timer與服務端發送心跳包保持長鏈接。

    如下爲整個登錄的流程:

Alt

2.6 異步實現

    異步的封裝有兩種實現方式:

  1. 須要更新界面的異步
  2. 不須要更新界面的異步

    兩種實現的方式是不一樣的。同時異步還有一個異步管理類。

2.6.1 TaskTrigger類

    TaskTrigger類主要用來註冊以及管理各個Task,每生成一個異步實例,都須要經過trigger接口,若是對於須要更新界面的異步,則直接調用AsyncTask的execute接口執行任務,不然將其放入Task的任務隊列中,後臺會經過process接口調用dotrigger接口執行具體的任務,執行結束後,調用task的callback接口。

2.6.2 須要更新界面的異步

    該方式主要集成Android自有的AsyncTask類,對其進行了一個簡單的封裝,該異步實現方式主要解決一些須要更新UI界面的異步,解決Android中非UI線程不能更新UI的問題。

2.6.3 不須要更新界面的異步

    該異步主要提供不須要更新UI的一些異步操做,該實現方式爲新開啓一個線程執行任務,當任務執行完成以後調用回調函數。


2.7 本地緩存

    存消息時,根據緩存中以前一條消息的ID,得到新消息的惟一自增ID,將該消息存入DB。

    存消息時依賴三張表,聯繫人表,主消息表和附加消息表。首先會根據當前收發用戶的ID從聯繫人表中獲得二者的惟一聯繫ID,並設置消息中的聯繫ID,將該消息存入主消息表和附加消息表。

Alt

    讀消息時,直接從DB中拉取便可,根據參數的不一樣拉取的消息不一樣。

    能夠根據消息ID,拉取某一天消息,也能夠根據起止消息ID,拉取指定偏移量和指定條數的消息列表。

Alt

    存用戶信息時,判斷該用戶信息是否爲空或合法,若爲空或不合法,直接返回;若合法,則更新 ,緩存中的用戶信息,用戶信息只保留在緩存中。

Alt

    取用戶信息時,先判斷緩存中是否存在該用戶信息,若存在,則直接返回該用戶信息;不然返回null,由業務端選擇是否發起取用戶信息的操做。

Alt

    收發消息時,獲得好友ID,判斷緩存中得最近聯繫人ID列表中是否存在,若不存在則添加,不然 忽略;

    一樣地,在獲取最近聯繫人列表時也如此。

Alt

    首先得到最近聯繫人ID列表,而後從緩存中讀取必要地信息,組合成最近聯繫人列表。

    另外經過異步監測有新消息來通知UI主線程來更新界面

Alt

相關文章
相關標籤/搜索