互聯網+時代,消息量級的大幅上升,消息形式的多元化,給即時通信雲服務平臺帶來了很是大的挑戰。高併發的IM系統背後究竟有着什麼樣的架構和特性?
以上內容由網易雲信首席架構師內部分享材料整理而成前端
相關閱讀推薦:java
推送保障及網絡優化詳解(一):如何實現不影響用戶體驗的後臺保活
推送保障及網絡優化詳解(二):如何作長鏈接加推送組合方案
推送保障及網絡優化詳解(三):如何在弱網環境下優化大數據傳輸?
本文要點:git
一、底層客戶端 SDK,覆蓋了安卓,iOS,windows PC 桌面端,web 網頁端和嵌入式設備等多個平臺。在 SDK 層使用的網絡協議有 4 層的 TCP 協議和基於 7 層的 Socket.IO 協議,後者專門用於 Web SDK 中提供長鏈接能力;除了集成到應用 App 中的 SDK 以外,還提供了供第三方服務器調用的 API 接口,基於 Http 協議;最後的 A/V SDK 是基於 UDP 協議的實時音視頻 SDK,用於實現基於網絡的語音和視頻通話。github
二、網關層:提供客戶端直接接入並維護與服務器之間的長鏈接;其中 WebSDK 直連的是 服務,這是一個基於 Socket.IO 協議實現的長鏈接服務,而供 AOS/IOS/PC等客戶端 SDK 直連的是基於 TCP 協議的 Link 服務;在 Link 和 WebLink 服務中承擔的一個很是重要的功能就是全部客戶端長鏈接的管理,後面基於 HTTP 協議上的網關有 API 服務,和 LBS 服務等,其中 LBS 服務用於幫助客戶端 SDK 選取最合適本身的網關接入點,優化網絡效率;而 API 服務則直接提供來自第三方服務器的業務請求;web
三、HA 層:在網關接入層之上是 HA 層,網關接入層可提供給客戶端直連,在 link 層和 Service 層之間有一個 HA 層用來解耦並提供高可用和易擴展等特性;在 HA 的具體實現方式上,對 Link 和 WebLink 這兩個維持客戶端長鏈接的服務,雲信提供了協議路由的服務,代爲分發業務請求,路由層會按照預約義的規則未來自客戶端的請求轉發到相應的業務節點上,當業務集羣擴容以後路由服務立刻能發現新的可用節點,並將請求轉發過去,當發現業務節點出現異常時也會被路由層標記並隔離下線以備替換。算法
四、業務節點集羣:在 HA 層上就是具體的業務節點集羣,咱們稱爲 App 服務,該服務處理具體的客戶端請求,後端直連 DB、cache 等各類基礎服務,這個集羣中的節點的特色是輕量,而且每一個節點都是無狀態的,雲信在實際部署這個集羣時會跨網絡環境部署,好比在同城雙機房中分別部署一套業務服務節點,前端經過路由層來分發業務請求,平時正常時業務互爲熱備,平均分擔線上的業務流量;當單一網絡環境或者基礎設施出現故障時立刻會被路由服務檢測到,並將該環境下的計算節點標記下線,將線上的流量請求所有轉發到正常工做的集羣中;從而提升了服務的總體可用性;配合監控平臺等運維工具,業務節點的實時處理能力和容量使用狀況都會被動態監測起來,當處理能力達到預設的水位線時會當即出發報警,運維人員能夠很是方便快捷得經過自動部署平臺對業務節點集羣進行擴容。segmentfault
五、業務層:其中包含了一些關鍵功能:核心的單聊消息、羣聊消息和聊天室,通知等;以及用戶信息託管,特殊關係管理等;還有面向 API 提供的如短信業務,回撥電話和專線會議等;還有實時音視頻和直播功能等相關能力。windows
最右邊列出的是從服務層上單獨列出來的更重要的功能,包括與開發者應用的第三方數據同步,個性化的內容審覈支持,超大羣服務,登錄登出事件日誌,漫遊消息和雲端消息歷史功能,推送服務等等。後端
經過如下這張簡化後的部署拓撲圖能夠對雲信總體技術體系的有初步瞭解。最右邊是客戶端,客戶端經過 LBS 服務獲取到網關接入點列表,再與Link和WebLink這類長鏈接服務器創建起長鏈接,並進行 RPC 操做,全部來自客戶端的請求都會經過路由層轉發到後端的 APP 層,App 層實時處理並下發同步請求的處理結果,並把一些異步任務經過隊列服務送到異步任務中,這些異步服務如大羣消息的發送,推送服務,雲端歷史消息的存儲和第三方的數據抄送同步服務等;在最下面的 API 接口上也是相似,API 直接提供給第三方的服務器調用請求,API 後端是各類獨立的業務,如回撥電話,短信等;一樣的全部的 API 後端業務請求也會產生相應的日誌;和 App 上的日誌樣,這些日誌都會被經過日誌採集平臺收集到大數據平臺中,一方面這類數據會存儲到 HDFS 上用於做爲數據統計分析的數據源;另外一方面會被導入到 Hbase 等數據倉庫中,用於提供日誌檢索和二次分析。瀏覽器
即時通信功能中最重要的鏈接管理服務怎麼作?消息快速到達的前提是客戶端和服務器之間保持了穩定的鏈接;能夠理解爲奠基雲信服務穩定性的基石。網關接入層須要解決的最重要的問題是什麼?核心依然是穩定,安全和快速。
如何保證穩定?網易雲信 SDK 採用長鏈接機制來實現,而且由心跳的方式來檢測斷線和自動作重連,同時雲信的 SDK 對移動網絡等弱網環境很是多的優化工做,對移動端/PC 端使用 TCP 來鏈接客戶端與服務器,對與 Web 端使用 socketIO 協議,實現長鏈接的同時解決瀏覽器的兼容性問題;
如何實現安全?,雲信要求全部在公網傳輸的數據都必須被加密;在 SDK 與服務器的鏈接創建過程當中有一個複雜的祕鑰協商過程,首先客戶端須要生成一個一次性使用的加密祕鑰,並使用非對稱加密方式將這個祕鑰加密以後傳給服務器,加密數據會被服務器解密,以後該加密祕鑰被保留在該長鏈接的會話信息中,數據來往均使用該祕鑰加密,這是一個流式加密,能夠有效防止中間人攻擊和數據包回放等攻擊手段。
如何保證快速?首先是在網關接入點的選擇上,藉助 LBS 服務能夠幫助客戶端尋找到最適合本身的網關接入點,好比從 IP 等信息判斷到的物理距離最近節點,其次在鏈接創建以後,長鏈接的機制能夠極大提高消息上下行的速度,而且在數據傳輸過程當中,雲信會對數據包壓縮傳輸,下降網絡開銷來升消息收發的速度;對頻繁的先後臺切換和重登錄這種移動客戶端場景,SDK 提供自動登陸和重連等機制,即在UI界面起來的同時已經提早把消息通道創建;在接入網關的選擇策略中,經過並行來提高鏈接創建的速度(展現圖);
SDK 接入的第一步是先請求 LBS 服務,獲取能夠進入的接入網關地址列表,LBS服務會根據多種策略條件來給客戶端分配地址,常見的條件以下:
1:appkey, 經過 appkey 能夠將一個特定的應用請求所有指向到一組特定的接入點,可用於專屬服務器方案;
2:客戶端 IP,用於根據客戶端所處的地理位置,爲其就近分配接入網關,常見於海外節點的配置;
在從 LBS 服務請求到接入網關地址以後,客戶端會按列表中的地址依次嘗試創建鏈接;若是嚴格按照這樣的順序,那客戶端創建鏈接的過程就會偏慢,爲了加速接入過程,實際上在操做時,SDK 都會使用本地緩存的最後一次 LBS 請求返回的地址列表來創建鏈接,同從 LBS 上拿一次新的地址列表緩存在本地,以備下次使用;當列表中的全部地址在嘗試過一遍均失效,則會使用默認的 link 地址來創建鏈接;默認地址也失敗是會出現 415 或者 408 這種網絡錯誤碼;
在獲取到目標地址以後就會嘗試創建 TCP 長鏈接,鏈接創建以後就會與服務器協商加密祕鑰,併發出第一個鑑權包,鑑權完成以後這個長鏈接就是一個安全有效的鏈接,客戶端能夠發起後續的 RPC 請求;服務器也能夠往這個鏈接上下發消息通知;若是祕鑰協商失敗或者鑑權失敗,這個鏈接就會被認爲是一個非法的鏈接請求,服務器會強制斷開;
最後聊一下加速節點的問題,爲了實現鏈接的快速,在網關接入點的分配時會優先距離該客戶端最近的節點;這裏將的加速節點就是爲了更靠近用戶提供的一種特殊節點。
加速節點的原理背景是運營商提供給我的用戶的線路,無論是移動網絡仍是有線網絡,其質量和 IDC 中心之間的網絡老是有差別的;若是將整個用戶鏈路中的關鍵路徑替換成 IDC 之間的網絡線路,那麼對提高鏈接的穩定性和速度是有幫助的。
假設一個處於美國的客戶經過手機網絡訪問位於杭州的一個網關接入點,因爲客戶端所在的網絡是一個移動網絡,直連到杭州服務器須要通過的鏈路很是長並且可能跳轉的中間節點不可預期,在中國來講,還要跨越防火牆;因此直連的狀況大部分可能就是沒法鏈接,或者鏈接以後頻繁斷線。
咱們提供了多層的加速節點:加入了加速節點以後,用戶的總體鏈路中原來不可預期的那段鏈路都換成了質量較好的線路,用戶直連到本地的加速節點的網絡每每就會好不少。
問題一:怎麼讓消息投遞併發能力倍增?
在這張圖中,上半部分表示的是一個點對點型的 Link 服務器,當發送者A發送一條消息以後,經過 link 這條消息提交到 App 中處理,App 中查詢到該消息接收者B所在的 link 服務器是 link y,因而向 link y 服務器下發一條下行通知包,link y 上再找到用戶 B 對應的長鏈接並將通知下發到客戶端;這種模式下,全部的接入點link 對於全部的用戶來講都是對等的,他能夠接入到任何一個服務器中,任何消息的發送都必須在業務層查詢到目標接收者所在的 link 服務器,並往相應的 link 服務器下發通知包,若是是一次羣發行爲,那就須要在業務 App 上把全部羣內的成員所在的 link 列表都查詢一遍;這是一個比較耗時的操做;而且是隨着消息接收成員的數量不斷上升開銷不斷增大;因此若是是須要往聊天室內發送消息,因爲聊天室內的成員數量很是龐大,這種模式很快就會遇到性能瓶頸,消息投遞的延時會很是嚴重;
對於廣播型的 link 服務器,雲信在分配接入點時首先遵循一個原則,那就是同個聊天室內的成員在分配聊天室時,儘可能分配在同一組接入點上;在 link 上維護了每一個房間內全部的成員的長鏈接集合;而在 App 上維護的再也不是特定用戶和 link 以前的映射關係,而是維護了特定房間分配的 link 的集合;因而在任何一個成員發出一條聊天室廣播消息以後,消息經過 link 上行到 App,App 只要找到該聊天室已經分配的 link 地址列表,往每一個 link 上下發一個廣播消息,link 在收到下行的廣播消息以後再在本地作廣播分發;這個效率比點播的模式高出了不止一個數量級;
問題二:怎麼解決單節點的性能瓶頸?
在講完了點對點型和廣播型這兩種 link 的區別以後;雲信再回頭來看看另一類基於 socket.io 實現的 weblink 的代理方案在雲信中的演變優化過程;
在這以前須要再強調下 WebLink 中兩個關鍵點,首先 WebLink 是基於 Socket.io 協議的,爲了保證數據通道的可靠,雲信須要使用Https來對通道加密,其次因爲是 Https 的請求因此必須提供獨立的域名。
圖一中顯示的是最先的方案,後端 Weblink 提供鏈接,並實現 SSL 加密,多個節點前面經過 LVS 作代理,域名綁定在 LVS 代理之上,LVS 代理之上再作 Keepalived 方案來保證 HA;這種方案對外暴露的域名只有一個,而內部實際有不少的節點,擴容對外也是透明的;Web 客戶端在鏈接時只須要直連這個惟一域名就能夠,對於單一產品來講這種方式最簡便快捷,客戶端能夠繞過地址分配的過程;缺點也集中在單一出口,若是這個單一出口受到 DDOS 攻擊,只能經過域名換綁來規避,而域名換綁須要必定的生效時間,帶來和一些運維上的代價,其次對於雲信這種服務來講,單一出口就喪失了靈活性;全部客戶直連到同一個入口,也沒法實現專屬服務和業務隔離,沒法實現加速節點方案;
因而便有了第二種方案,這種方案借鑑了 link 業務中的 LBS 分配的方式,仍是在 Weblink 節點上實現 SSL 加密,併爲每一個 Weblink 節點分配獨立域名,客戶端在接入前先經過LBS服務來分配到合適的接入點;這種方案好處就是提供了更大的靈活性,隨時能夠給集羣擴容,也能夠動態調整特定應用的接入點地址,也提供作加速節點的可能性;可是這種方案的問題是每一個節點都是單點,並且節點內仍是須要作 SSL 編碼,因爲 java 的 SSL 對 CPU 資源開銷比較大,在突發用戶流量是會影響單個節點的服務能力;
因而又有了第三種方案,這種方案前端使用Nginx作七層代理,並在 Nginx 配置 SSL 和域名綁定,後端能夠同時使用一組 Weblink;因爲使用了 Nginx,在端口的分配邏輯上也更加科學,提升了運維的便捷性;最後雲信就獲得了目前在使用的一個組合方案,前端仍是經過 LBS 服務來爲 SDK 分配接入點,以此提供靈活性;後端使用多個 Nginx 集羣作代理集羣,每一個集羣分組的性能都獲得了提升。
前面重點介紹了雲信在客戶端接入層的實現和接入點的管理上使用的一些方法,經過這些技術手段爲 IM 服務創建了一條穩定可靠的消息通道,如今來聊聊在業務層作的服務化和高可用上面的工做。
網關接入層負責客戶端長鏈接的維護和管理,全部的接入節點甚至能夠是無狀態的對等節點,只負責客戶端與服務器之間請求的傳遞的轉發,並優化轉發效率;而真正的業務處理邏輯仍是須要有業務層來實現。
業務層須要處理大量請求並負責和 DB,緩存,隊列,第三方接口等組件的交互,其穩定性,可用性和擴展能力直接影響了整個雲服務的質量;爲了使業務層具備更好的彈性,雲信在網關接入層和業務層之間引入了一個路由層來解耦;業務節點在上線以後會將本身註冊到服務中心,路由節點會轉接網關層的請求包,並從服務節點中挑選匹配的節點分發請求;這種三層架構使系統總體具備更好的彈性。
爲了提升業務的可用性,雲信會將業務節點分佈到分屬於不一樣網絡的環境中,正常狀況下能夠同時提供服務,一旦其中一個環境的網絡或者基礎設施出現故障,就能夠快速得經過路由層來將故障集羣下線。
靈活支持灰度升級模式,雲信能夠將其中部分業務節點升級,而後經過路由層的配置將指定的用戶流量導入到新升級的節點中;
專屬服務的靈活支持,對於一些對資源獨佔需求比較強烈的客戶,雲信能夠經過路由層將該客戶應用下的全部流量導入到獨立的集羣中。
隨着即時通信以及音頻處理和壓縮技術的不斷髮展,效果更好、適用範圍更廣、性能更高的算法和新的技術必將不斷涌現,若是你有好的技術或者分享,歡迎關注網易雲信官方博客和 GitHub:
關注更多技術乾貨內容: 網易雲信博客
歡迎關注 網易雲信 GitHub
歡迎關注 網易雲信官網