<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

    本文由公眾號“后臺技術匯”分享,原題“基于實踐,設計一個百萬級別的高可用 & 高可靠的 IM 消息系統”,原文鏈接在文末。由于原文存在較多錯誤和不準確內容,有大量修訂和改動。

    1、引言

    大家好,我是公眾號“后臺技術匯”的博主“一枚少年”。

    本人從事后臺開發工作 3 年有余了,其中讓我感觸最深刻的一個項目,就是在兩年前從架構師手上接過來的 IM 消息系統。

    本文內容將從開發者的視角出發(主要是我自已的開發體會),圍繞項目背景、業務需求、技術原理、開發方案等主題,一步一步的與大家一起剖析:設計一套百萬消息量的小規模IM系統架構設計上需要注意的技術要點。

    學習交流:

    - 即時通訊/推送技術開發交流5群:215477170 [推薦]

    - 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

    - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK 

    本文同步發布于:http://www.52im.net/thread-3752-1-1.html

    2、項目背景

    我們仔細觀察就能發現,生活中的任何類型互聯網服務都有 IM 系統的存在。

    比如:

    • 1)基礎性服務類-騰訊新聞(評論消息);
    • 2)商務應用類-釘釘(審批工作流通知);
    • 3)交流娛樂類-QQ/微信(私聊群聊 &討論組 &朋友圈);
    • 4)互聯網自媒體-抖音快手(點贊打賞通知)。

    在這些林林總總的互聯網生態產品里,即時消息系統作為底層能力,在確保業務正常與用戶體驗優化上,始終扮演了至關重要的角色。

    所以,現如今的互聯網產品中,即時通訊技術已經不僅限于傳統IM聊天工具本身,它早已通過有形或無形的方式嵌入到了各種形式的互聯網應用當中。IM技術(或者說即時通訊技術)對于很多開發者來說,確實是必不好可少的領域知識,不可或缺。

    3、系統能力

    典型的IM系統通常需要滿足四點能力:高可靠性、高可用性、實時性和有序性。

     

    這幾個概念我就不詳細展開,如果你是IM開發入門者,可以詳讀下面這幾篇:

    4、架構設計

    以我的這個項目來說,架構設設計要點主要是:

    • 1)微服務:拆分為用戶微服務 &消息連接服務 &消息業務服務;
    • 2)存儲架構:兼容性能與資源開銷,選擇 reids&mysql;
    • 3)高可用:可以支撐起高并發場景,選擇 Spring 提供的 websocket;
    • 4)支持多端消息同步:app 端、web 端、微信公眾號、小程序消息;
    • 5)支持在線與離線消息場景。

    業務架構圖主要是這樣:

    技術模塊分層架構大概是這樣:

    5、消息存儲技術要點

    5.1 理解讀擴散和寫擴散

    5.1.1)基本概念:

    我們舉個例子說明什么是讀擴散,什么是寫擴散:

    一個群聊“相親相愛一家人”,成員:爸爸、媽媽、哥哥、姐姐和我(共 5 人)。

    因為你最近交到女朋友了,所以發了一條消息“我脫單了”到群里面,那么自然希望爸爸媽媽哥哥姐姐四個親人都能收到了。

    正常邏輯下,群聊消息發送的流程應該是這樣:

    • 1)遍歷群聊的成員并發送消息;
    • 2)查詢每個成員的在線狀態;
    • 3)成員不在線的存儲離線;
    • 4)成員在線的實時推送。

    數據分發模型如下:

    問題在于:如果第4步發生異常,群友會丟失消息,那么會導致有家人不知道“你脫單了”,造成催婚的嚴重后果。

    所以優化的方案是:不管群員是否在線,都要先存儲消息。

    按照上面的思路,優化后的群消息流程如下:

    • 1)遍歷群聊的成員并發送消息;
    • 2)群聊所有人都存一份;
    • 3)查詢每個成員的在線狀態;
    • 4)在線的實時推送。

    以上優化后的方案,便是所謂的“寫擴散”了。

    問題在于:每個人都存一份相同的“你脫單了”的消息,對磁盤和帶寬造成了很大的浪費(這就是寫擴散的最大弊端)。

    所以優化的方案是:群消息實體存儲一份,用戶只存消息 ID 索引。

    于是再次優化后的發送群消息流程如下:

    • 1)遍歷群聊的成員并發送消息;
    • 2)先存一份消息實體;
    • 3)然后群聊所有人都存一份消息實體的 ID 引用;
    • 4)查詢每個成員的在線狀態;
    • 5)在線的實時推送。

    二次優化后的方案,便是所謂的“讀擴散”了。

    5.1.2)小結一下:

    • 1)讀擴散:讀取操作很重,寫入操作很輕,資源消耗相對小一些;
    • 2)寫擴散:讀取操作很輕,寫入操作很重,資源消耗相對大一些。

    從公開的技術資料來看,微信和釘釘的群聊消息應該使用的是寫擴散方式,具體可以參看這兩篇:《微信后臺團隊:微信后臺異步消息隊列的優化升級實踐分享》、《阿里IM技術分享(四):閑魚億級IM消息系統的可靠投遞優化實踐》(注意“5.5 服務端存儲模型優化”這一節)。

    5.2 “消息”所關聯的對象

    5.2.1)消息實體模型:

    常見的消息業務,可以抽象為幾個實體模型概念:用戶/用戶關系/用戶設備/用戶連接狀態/消息/消息隊列。

    在IM系統中的實體模型關系大致如下:

    5.2.2)實體模型概念解釋:

    用戶實體:

    • 1)用戶->用戶終端設備:每個用戶能夠多端登錄并收發消息;
    • 2)用戶->消息:考慮到讀擴散,每個用戶與消息的關系都是 1:n;
    • 3)用戶->消息隊列:考慮到讀擴散,每個用戶都會維護自己的一份“消息列表”(1:1),如果考慮到擴容,甚至可以開辟一份消息溢出列表接收超出“消息列表”容量的消息數據(此時是 1:n);
    • 4)用戶->用戶連接狀態:考慮到用戶能夠多端登錄,那么 app/web 都會有對應的在線狀態信息(1:n);
    • 5)用戶->聯系人關系:考慮到用戶最終以某種業務聯系到一起,組成多份聯系人關系,最終形成私聊或者群聊(1:n);

    聯系人關系(主要由業務決定用戶與用戶之間的關系),比如說:

    • 1)某個家庭下有多少人,這個家庭群聊就有多少人;
    • 2)在 ToB 場景,在釘釘企業版里,我們往往有企業群聊這個存在。

    消息實體:

    消息->消息隊列:考慮到讀擴散,消息最終歸屬于一個或多個消息隊列里,因此群聊場景它會分布在不同的消息隊列里。

    消息隊列實體:

    消息隊列:確切說是消息引用隊列,它里面的索引元素最終指向具體的消息實體對象。

    用戶連接狀態:

    • 1)對于 app 端:網絡原因導致斷線,或者用戶手動 kill 掉應用進程,都屬于離線;
    • 2)對于 web 端:網絡原因導致瀏覽器斷網,或者用戶手動關閉標簽頁,都屬于離線;
    • 3)對于公眾號:無法分別離線在線;
    • 4)對于小程序:無法分別離線在線。

    用戶終端設備:

    客戶端一般是 Android&IOS,web 端一般是瀏覽器,還有其他靈活的 WebView(公眾號/小程序)。

    5.3 消息的存儲方案

    對于消息存儲方案,本質上只有三種方案:要么放在內存、要么放在磁盤、要么兩者結合存儲(據說大公司為了優化性能,活躍的消息數據都是放在內存里面的,畢竟有錢~)。

    下面分別解析主要方案的優點與弊端:

    • 1)方案一:考慮性能,數據全部放到 redis 進行存儲;
    • 2)方案二:考慮資源,數據用 redis + mysql 進行存儲。

    5.3.1)對于方案一:redis

    前提:用戶 & 聯系人關系,由于是業務數據,因此統一默認使用關系型數據庫存儲。

    流程圖:

    解釋如下:

    • 1)用戶發消息;
    • 2)redis 創建一條實體數據 &一個實體數據計時器;
    • 3)redis 在 B 用戶的用戶隊列 添加實體數據引用;
    • 4)B 用戶拉取消息(后續 5.2 會提及拉模式)。

    實現方案:

    • 1)用戶隊列,zset(score 確保有序性);
    • 2)消息實體列表,hash(msg_id 確保唯一性);
    • 3)消息實體計數器,hash(支持群聊消息的引用次數,倒計時到零時則刪除實體列表的對應消息,以節省資源)。

    優點是:內存操作,響應性能好

    弊端是:

    • 1)內存消耗巨大,eg:除非大廠,小公司的服務器的寶貴內存資源是耗不起業務的,隨著業務增長,不想拓展資源,就需要手動清理數據了;
    • 2)受 redis 容災性策略影響較大,如果 redis 宕機,直接導致數據丟失(可以使用 redis 的集群部署/哨兵機制/主從復制等手段解決)。

    5.3.2)方案二:redis+mysql

    前提:用戶 & 聯系人關系,由于是業務數據,因此統一默認使用關系型數據庫存儲。

    流程圖:

    解釋如下:

    • 1)用戶發消息;
    • 2)mysql 創建一條實體數據;
    • 3)redis 在 B 用戶的用戶隊列 添加實體數據引用;
    • 4)B 用戶拉取消息(下文會提及拉模式)。

    實現方案:

    • 1)用戶隊列,zset(score 確保有序性);
    • 2)消息實體列表,轉移到 mysql(表主鍵 id 確保唯一性);
    • 3)消息實體計數器,hash(刪除這個概念,因為磁盤可用總資源遠遠高于內存總資源,哪怕一直存放 mysql 數據庫,在業務量百萬級別時也不會有大問題,如果是巨大體量業務就需要考慮分表分庫處理檢索數據的性能了)。

    優點是:

    • 1)抽離了數據量最大的消息實體,大大節省了內存資源;
    • 2)磁盤資源易于拓展 ,便宜實用。

    弊端是:磁盤讀取操作,響應性能較差(從產品設計的角度出發,你維護的這套 IM 系統究竟是強 IM 還是弱 IM)。

    6、消息的消費模式

    6.1 拉模式

    選用消息拉模式的原因:

    • 1)由于用戶數量太多(觀察者),服務器無法一一監控客戶端的狀態,因此消息模塊的數據交互使用拉模式,可以節約服務器資源;
    • 2)當用戶有未讀消息時,由客戶器主動發起請求的方式,可以及時刷新客戶端狀態。

    6.2 ack 機制

    技術原理:

    • 1)基于拉模式實現的數據拉取請求(第一次 fetch 接口)與數據拉取確認請求(第二次 fetch 接口)是成對出現的;
    • 2)客戶端二次調用 fetch 接口,需要將上次消息消費的錨點告訴服務端,服務器進而刪除已讀消息。

    請求模型原理圖如下:

    實現方案1:基于每一條消息編號 ACK:

    • 1)實現:客戶端在接收到消息之后,發送 ACK 消息編號給服務端,告知已經收到該消息。服務端在收到 ACK 消息編號的時候,標記該消息已經發送成功;
    • 2)弊端:這種方案,因為客戶端逐條 ACK 消息編號,所以會導致客戶端和服務端交互次數過多。當然,客戶端可以異步批量 ACK 多條消息,從而減少次數。

    實現方案2:基于滑動窗口 ACK:

    1)客戶端在接收到消息編號之后,和本地的消息編號進行比對:

     - 如果比本地的小,說明該消息已經收到,忽略不處理;

     - 如果比本地的大,使用本地的消息編號,向服務端拉取大于本地的消息編號的消息列表,即增量消息列表。

     - 拉取完成后,更新消息列表中最大的消息編號為新的本地的消息編號;

    2)服務端在收到 ack 消息時,進行批量標記已讀或者刪除。

    這種方式,在業務被稱為推拉結合的方案,在分布式消息隊列、配置中心、注冊中心實現實時的數據同步,經常被采用。

    6.3 基于ack 機制的好處

    第一次獲取消息完成之后,如果沒有 ack 機制,流程是:

    • 1)服務器刪除已讀消息數據;
    • 2)服務端把數據包響應給客戶端。

    如果由于網絡延遲,導致客戶端長時間取不到數據,這時客戶端會斷開該次 HTTP 請求,進而忽略這次響應數據的處理,最終導致消息數據被刪除而后續無法恢復。

    有了 ack 機制,哪怕第一次獲取消息失敗,客戶端還是可以繼續請求消息數據,因為在 ack 確認之前,消息數據都不會刪除掉。

    7、微服務設計

    一般來說 IM 微服務,能拆分為基礎的三個微服務:

    • 1)用戶服務;
    • 2)業務服務;
    • 3)連接管理服務。

    參考架構圖:

    他們分工合作如下。

    用戶微服務(用戶設備的登錄 & 登出):

    • 1)設備號存庫;
    • 2)連接狀態更新;
    • 3)其他登錄端用戶踢出等。

    連接管理微服務:

    • 1)狀態保存:保存用戶設備長連接對象;
    • 2)剔除無效連接:輪訓已有長連接對象狀態,超時刪除對象;
    • 3)接受客戶端的心跳包:刷新長連接對象的狀態。

    消息業務微服務:

    • 1)消息存儲:進行私聊/群聊的消息存儲策略(請參看“消息存儲模型”一節);
    • 2)消息消費:進行消息獲取響應與 ack 確認刪除(請參看“消息消費模式”一節);
    • 3)消息路由:用戶在線時,路由消息通知包到“消息連接管理微服務”,以通知用戶客戶端來取消息。

    最后提一下消息的路由:

    微服務之間也有通信手段,比如業務服務到連接管理服務,兩者之間可以通過 RPC 實現實時消息的路由通知。

    8、離線消息推送

    離線推送方案上,大家一般都會考慮采用兩種方案:

    • 1)企業自研后臺離線 PUSH 系統;
    • 2)企業自行對接第三方手機廠商 PUSH 系統。

    8.1 企業自研后臺離線 PUSH 系統

    技術原理:

    在應用級別,客戶端與后臺離線 PUSH 系統保持長連接,當用戶狀態被檢測為離線時,通過這個長連接告知客戶端“有新消息”,進而喚醒手機彈窗標題。

    弊端就是:

    隨著安卓和蘋果系統的限制越來越嚴格,一般客戶端的活動周期被限制的死死的,一旦客戶端進程被挪到后臺就立馬被 kill 掉了,導致客戶端保活特別難做好(這也是很多中小企業頭疼的地方,畢竟只有微信或者 QQ 這種體量的一級市場 APP,手機系統愿意給他們留后門來做保活)。具體可以讀一下《Android P正式版即將到來:后臺應用保活、消息推送的真正噩夢》這篇。

    8.2 企業自行對接第三方廠商 PUSH 系統

    技術原理:

    在系統級別,每個硬件系統都會與對應的手機廠商保持長連接,當用戶狀態被檢測為離線時,后臺將推送報文通過 HTTP 請求,告知第三方手機廠商服務器,進而通過系統喚醒 app 的彈窗標題。

    弊端就是:

    • 1)作為應用端,消息是否確切送達給用戶側,是未知的;推送的穩定性也取決于第三方手機廠商的服務穩定性;
    • 2)額外進行 sdk 的對接工作,增加了工作量;
    • 3)第三方廠商隨時可能升級 sdk 版本,導致沒有升級 sdk 的服務器出現推送失敗的情況,給 Sass 系統部署帶來困難;
    • 4)推送證書配置也要考慮到維護成本。

    總之,IM里離線消息推送是個很頭疼的問題(當然這里主要說是Andriod了,iOS里蘋果官方的APNs就舒服多了),有興趣好一讀一下下面這些文章:

    1. 全面盤點當前Android后臺保活方案的真實運行效果(截止2019年前)
    2. 融云技術分享:融云安卓端IM產品的網絡鏈路保活技術實踐
    3. 2020年了,Android后臺保活還有戲嗎?看我如何優雅的實現!
    4. 史上最強Android保活思路:深入剖析騰訊TIM的進程永生技術
    5. Android進程永生技術終極揭密:進程被殺底層原理、APP應對被殺技巧
    6. Android保活從入門到放棄:乖乖引導用戶加白名單吧(附7大機型加白示例)
    7. 阿里IM技術分享(六):閑魚億級IM消息系統的離線推送到達率優化

    9、其它需要考慮的技術要點

    9.1 安全性

    關于IM安全性,我個人的體會是這樣:

    • 1)業務數據傳輸安全性使用 https 訪問;
    • 2)實時消息使用SSL/TLS對長連接進行加密;
    • 3)使用私有協議,不容易解析;
    • 4)內容安全性端到端加密,中間任何環節都不能解密(即發送和接收端交換互相的密鑰來解密,服務器端解密不了);
    • 5)服務器端不存儲消息。

    以上要點中:IM中的長連接安全性是比較重要且不容易處理的,因為它需要在安全性和性能上作平衡和取舍(不能光顧著安全而損失IM長連接的高吞吐性能),這方面可以參考微信團隊分享的這篇《微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解》。

    另外:更高安全性的場景可以考慮組合加密方案,詳情可以參考《探討組合加密算法在IM中的應用》。

    9.2 一致性

    IM消息一致性難題,主要是保證消息不亂序的問題。這個話題,初學者可以讀讀這篇《零基礎IM開發入門(四):什么是IM系統的消息時序一致性?》,我就不再贅述了。

    解決一致性問題的切入點有很多,最常見的是使用有序的消息唯一id,關于有序且唯一的ID生成問題,微信團隊的思路就很好,可以借鑒一下《微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)》。

    另外,以下幾篇關于消息有序性問題的總結也非常好,可以進行參考:

    1. 如何保證IM實時消息的“時序性”與“一致性”?
    2. 一個低成本確保IM消息時序的方法探討
    3. 一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等

    9.3 可靠性

    IM里所謂的可靠性,說直白一點就是保證消息不丟失,這看似理所當然、稀松平常的技術點,在IM系統中又是另一個很大的話題,鑒于本人水平有限,就不班門弄斧,IM初學者可以能過《零基礎IM開發入門(三):什么是IM系統的可靠性?》這篇來理解可靠性這個概念。

    然后再讀讀《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》、《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》這兩篇,基本上就能對IM可靠性這個技術要點有了比較深刻的認識了。

    下面這幾篇實戰性的總結,適合有一定IM經驗的同行們學習,可以借鑒學習一下:

    1. 融云技術分享:全面揭秘億級IM消息的可靠投遞機制
    2. IM開發干貨分享:如何優雅的實現大量離線消息的可靠投遞
    3. 從客戶端的角度來談談移動端IM的消息可靠性和送達機制
    4. 阿里IM技術分享(四):閑魚億級IM消息系統的可靠投遞優化實踐

    9.4 實時性

    IM實時性這個技術點,就回歸到了“即時通訊”這個技術的立身之本了,可以說,沒有實時性,也就不存在“即時通訊”這個技術范疇了,可以見它的重要性。關于實時性這個概念,初學者可以通過《零基礎IM開發入門(二):什么是IM系統的實時性?》這篇去學習一下,我就不啰嗦了,人家比我說的好。

    筆者公司的項目里實時通信用方案都是采用 WebSocket(如果你不了解WebSocket,可以讀一下《WebSocket從入門到精通,半小時就夠!》,以及《搞懂現代Web端即時通訊技術一文就夠:WebSocket、socket.io、SSE),但是某些低版本的瀏覽器可能不支持 WebSocket,所以實際開發時,要兼容前端所能提供的能力進行方案設計。

    以下兩篇關于實時性的同行實踐性總結也不錯:

    1. 移動端IM中大規模群消息的推送如何保證效率、實時性?
    2. 阿里IM技術分享(五):閑魚億級IM消息系統的及時性優化實踐

    10、我在項目實踐中的體會

    作為研發者,有兩年多的時間都在維護迭代公司的 IM 消息系統,以下是我自已的小小體會。

    我體會到的重點難點有以下幾方面:

    • 1)業務閉環:消息是如何寫入存儲、消息是如何消費掉、在線消息是如何實現、離線消息是如何實現、群聊/私聊有何不一樣、多端消息如何實現;
    • 2)解 Bug 填坑:在線消息收不到,第三方推送證書如何配置;
    • 3)代碼優化:單體架構拆分微服務;
    • 4)存儲優化:1.0 版本的 redis 存儲到 2.0 版本的 redis+mysql;
    • 5)性能優化:未讀提醒等接口性能優化。

    項目還存在可優化的地方:

    • 1)高可用方案之一:是部署多部連接管理服務器,以支撐更多的用戶連接;
    • 2)高可用方案之二:是對單部連接管理服務,使用 Netty 進行框架層優化,讓一個服務器支撐更多的用戶連接;
    • 3)消息量劇增時:可以考慮對消息存儲作進一步優化;
    • 4)消息冷熱部署:不同的地區會存在業務量差異,比如在某些經濟發達的省份,IM 系統面臨的壓力會比較大,一些欠發達省份,服務壓力會低一點,所以這塊可以考慮數據的冷熱部署。

    11、寫在最后

    兩年前從架構師手上接過來的 IM 消息系統模塊,讓我逐步培養了架構思維,見賢思齊,感謝恩師。

    IM技術是個經久不衰的領域,但同時可直接使用的技術資產也非常匱乏,必竟傳統的IM巨頭們的產品通常都是私有化協議、私有化方案,很難有業界共同的方案可以直接使用(包括資料或開源代碼),正是這種不通用、不準,間接導致IM技術門檻的提高。所以通常公司要搞IM的話,如果沒有技術積累,就只能從零開始造輪子。

    為了改變這種局面,也希望搞IM開發的同學不要悶頭造車,應該多多借鑒同行的思路,同時也能積極分享自已的經驗,讓IM開發不再痛苦。

    以上拋磚引玉,歡迎留言討論,一起進步。

    12、參考資料

    [1] 新手入門一篇就夠:從零開發移動端IM

    [2] 為何基于TCP協議的移動端IM仍然需要心跳保活機制?

    [3] Android P正式版即將到來:后臺應用保活、消息推送的真正噩夢

    [4] WebSocket從入門到精通,半小時就夠!

    [5] 搞懂現代Web端即時通訊技術一文就夠:WebSocket、socket.io、SSE

    [6] 一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)

    [7] 一套原創分布式即時通訊(IM)系統理論架構方案

    [8] 一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐

    [9] 微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)

    [10] 阿里IM技術分享(四):閑魚億級IM消息系統的可靠投遞優化實踐

    [11] 阿里IM技術分享(五):閑魚億級IM消息系統的及時性優化實踐

    [12] 一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等

    [13] 從新手到專家:如何設計一套億級消息量的分布式IM系統

    [14] 企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等

    [15] 融云技術分享:全面揭秘億級IM消息的可靠投遞機制

    [16] 即時通訊安全篇(六):非對稱加密技術的原理與應用實踐

    [17] 通俗易懂:一篇掌握即時通訊的消息傳輸安全原理

    [18] 微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解

    [19] 零基礎IM開發入門(二):什么是IM系統的實時性?

    [20] 零基礎IM開發入門(三):什么是IM系統的可靠性?

    [21] 零基礎IM開發入門(四):什么是IM系統的消息時序一致性?

    本文已同步發布于“即時通訊技術圈”公眾號。

    同步發布鏈接是:http://www.52im.net/thread-3752-1-1.html



    作者:Jack Jiang (點擊作者姓名進入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時通訊開發交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 亚洲丶国产丶欧美一区二区三区| 毛片亚洲AV无码精品国产午夜| 无人在线观看完整免费版视频| 亚洲欧美第一成人网站7777| 亚洲精品无码日韩国产不卡?V| 久久午夜无码免费| 亚洲一区二区无码偷拍| 中文字幕亚洲无线码a| 99久久免费精品高清特色大片| 亚洲中文字幕无码爆乳| 亚洲热妇无码AV在线播放| 桃子视频在线观看高清免费完整| 一级黄色免费网站| 亚洲伊人久久大香线蕉影院| 亚洲国产综合无码一区二区二三区| 精品无码无人网站免费视频 | 久久国产成人亚洲精品影院 | 色偷偷尼玛图亚洲综合| 亚洲精品午夜国产VA久久成人| 国产精品美女午夜爽爽爽免费| 中文字幕免费视频精品一| 亚洲综合一区国产精品| 亚洲成人午夜在线| 亚洲精品国产福利一二区| 皇色在线视频免费网站| 国产成人免费ā片在线观看老同学| 亚洲人成电影网站免费| 久久亚洲AV成人出白浆无码国产| 日产国产精品亚洲系列| 国产va免费精品观看精品| 免费毛片在线看不用播放器| 国产成人亚洲精品电影| 亚洲国产品综合人成综合网站| 亚洲精品蜜桃久久久久久| 国产一区二区三区免费看| 无码少妇一区二区浪潮免费 | 亚洲免费一区二区| 大学生一级特黄的免费大片视频| 99久久免费精品视频| 成人无码a级毛片免费| 一级全免费视频播放|