互聯網+時代,消息量級的大幅上升,消息形式的多元化,給即時通訊雲服務平台帶來了非常大的挑戰。高並發的IM系統背后究竟有着什么樣的架構和特性?
以上內容由網易雲信首席架構師內部分享材料整理而成
相關閱讀推薦:
本文要點:
l 網易雲信整體架構解析
l 雲信中的客戶端連接和接入點管理
l 服務化和高可用
網易IM雲分層架構圖解析
1,底層客戶端SDK,覆蓋了安卓,iOS,windows PC桌面端,web網頁端和嵌入式設備等多個平台。在SDK層使用的網絡協議有4層的TCP協議和基於7層的Socket.IO協議,后者專門用於Web SDK中提供長連接能力;除了集成到應用App中的SDK之外,還提供了供第三方服務器調用的API接口,基於Http協議;最后的A/V SDK是基於UDP協議的實時音視頻SDK,用於實現基於網絡的語音和視頻通話。
(網易IM雲分層架構圖)
2,網關層:提供客戶端直接接入並維護與服務器之間的長連接;其中WebSDK直連的是Weblink服務,這是一個基於Socket.IO協議實現的長連接服務,而供AOS/IOS/PC等客戶端SDK直連的是基於TCP協議的Link服務;在Link和WebLink服務中承擔的一個非常重要的功能就是所有客戶端長連接的管理,后面基於HTTP協議上的網關有API服務,和LBS服務等,其中LBS服務用於幫助客戶端SDK選取最合適自己的網關接入點,優化網絡效率;而API服務則直接提供來自第三方服務器的業務請求;
3,HA層:在網關接入層之上是HA層,網關接入層可提供給客戶端直連,在link層和Service層之間有一個HA層用來解耦並提供高可用和易擴展等特性;在HA的具體實現方式上,對Link和WebLink這兩個維持客戶端長連接的服務,雲信提供了協議路由的服務,代為分發業務請求,路由層會按照預定義的規則將來自客戶端的請求轉發到相應的業務節點上,當業務集群擴容之后路由服務馬上能發現新的可用節點,並將請求轉發過去,當發現業務節點出現異常時也會被路由層標記並隔離下線以備替換。
4,業務節點集群:在HA層上就是具體的業務節點集群,我們稱為App服務,該服務處理具體的客戶端請求,后端直連DB、cache等各種基礎服務,這個集群中的節點的特點是輕量,並且每個節點都是無狀態的,雲信在實際部署這個集群時會跨網絡環境部署,比如在同城雙機房中分別部署一套業務服務節點,前端通過路由層來分發業務請求,平時正常時業務互為熱備,平均分擔線上的業務流量;當單一網絡環境或者基礎設施出現故障時馬上會被路由服務檢測到,並將該環境下的計算節點標記下線,將線上的流量請求全部轉發到正常工作的集群中;從而提高了服務的整體可用性;配合監控平台等運維工具,業務節點的實時處理能力和容量使用情況都會被動態監測起來,當處理能力達到預設的水位線時會立即出發報警,運維人員可以非常方便快捷得通過自動部署平台對業務節點集群進行擴容。
5,業務層:其中包含了一些關鍵功能:核心的單聊消息、群聊消息和聊天室,通知等;以及用戶信息托管,特殊關系管理等;還有面向API提供的如短信業務,回撥電話和專線會議等;還有實時音視頻和直播功能等相關能力。
最右邊列出的是從服務層上單獨列出來的更重要的功能,包括與開發者應用的第三方數據同步,個性化的內容審核支持,超大群服務,登陸登出事件日志,漫游消息和雲端消息歷史功能,推送服務等等。
網易IM雲部署拓撲
通過以下這張簡化后的部署拓撲圖可以對雲信整體技術體系的有初步了解。最右邊是客戶端,客戶端通過LBS服務獲取到網關接入點列表,再與Link和WebLink這類長連接服務器建立起長連接,並進行RPC操作,所有來自客戶端的請求都會通過路由層轉發到后端的APP層,APP層實時處理並下發同步請求的處理結果,並把一些異步任務通過隊列服務送到異步任務中,這些異步服務如大群消息的發送,推送服務,雲端歷史消息的存儲和第三方的數據抄送同步服務等;在最下面的API接口上也是類似,API直接提供給第三方的服務器調用請求,API后端是各種獨立的業務,如回撥電話,短信等;同樣的所有的API后端業務請求也會產生相應的日志;和APP上的日志樣,這些日志都會被通過日志采集平台收集到大數據平台中,一方面這類數據會存儲到HDFS上用於作為數據統計分析的數據源;另一方面會被導入到Hbase等數據倉庫中,用於提供日志檢索和二次分析。
(網易IM雲部署拓撲示意)
高並發IM系統連接層的優化實踐
即時通訊功能中最重要的連接管理服務怎么做?消息快速到達的前提是客戶端和服務器之間保持了穩定的連接;可以理解為奠定雲信服務穩定性的基石。網關接入層需要解決的最重要的問題是什么?核心依然是穩定,安全和快速。
如何保證穩定?網易雲信SDK采用長連接機制來實現,並且由心跳的方式來檢測斷線和自動做重連,同時雲信的SDK對移動網絡等弱網環境非常多的優化工作,對移動端/PC端使用TCP來連接客戶端與服務器,對與Web端使用socketIO協議,實現長連接的同時解決瀏覽器的兼容性問題;
如何實現安全?,雲信要求所有在公網傳輸的數據都必須被加密;在SDK與服務器的連接建立過程中有一個復雜的秘鑰協商過程,首先客戶端需要生成一個一次性使用的加密秘鑰,並使用非對稱加密方式將這個秘鑰加密之后傳給服務器,加密數據會被服務器解密,之后該加密秘鑰被保留在該長連接的會話信息中,數據來往均使用該秘鑰加密,這是一個流式加密,可以有效防止中間人攻擊和數據包回放等攻擊手段。
如何保證快速?首先是在網關接入點的選擇上,借助LBS服務可以幫助客戶端尋找到最適合自己的網關接入點,比如從ip等信息判斷到的物理距離最近節點,其次在連接建立之后,長連接的機制可以極大提升消息上下行的速度,並且在數據傳輸過程中,雲信會對數據包壓縮傳輸,降低網絡開銷來升消息收發的速度;對頻繁的前后台切換和重登陸這種移動客戶端場景,SDK提供自動登錄和重連等機制,即在UI界面起來的同時已經提前把消息通道建立;在接入網關的選擇策略中,通過並行來提升連接建立的速度(展示圖);
客戶端與服務器建立長連接的過程展現
SDK接入的第一步是先請求LBS服務,獲取可以進入的接入網關地址列表,LBS服務會根據多種策略條件來給客戶端分配地址,常見的條件如下:
1:appkey, 通過appkey可以將一個特定的應用請求全部指向到一組特定的接入點,可用於專屬服務器方案;
2:客戶端ip,用於根據客戶端所處的地理位置,為其就近分配接入網關,常見於海外節點的配置;
3. SDK版本號,將特定版本范圍的客戶端指向到特定網關,常用於新老版本升級的兼容方案,目前無實際使用案例;
4. 特定環境標識,如智能客服環境等,用於將特定類型的app指向到特定網關,用於較大粒度環境隔離需求;
在從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,緩存,隊列,第三方接口等組件的交互,其穩定性,可用性和擴展能力直接影響了整個雲服務的質量;為了使業務層具有更好的彈性,雲信在網關接入層和業務層之間引入了一個路由層來解耦;業務節點在上線之后會將自己注冊到服務中心,路由節點會轉接網關層的請求包,並從服務節點中挑選匹配的節點分發請求;這種三層架構使系統整體具有更好的彈性。
為了提高業務的可用性,雲信會將業務節點分布到分屬於不同網絡的環境中,正常情況下可以同時提供服務,一旦其中一個環境的網絡或者基礎設施出現故障,就可以快速得通過路由層來將故障集群下線。
靈活支持灰度升級模式,雲信可以將其中部分業務節點升級,然后通過路由層的配置將指定的用戶流量導入到新升級的節點中;
專屬服務的靈活支持,對於一些對資源獨占需求比較強烈的客戶,雲信可以通過路由層將該客戶應用下的所有流量導入到獨立的集群中。