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