《IM 技術在多應用場景下的實現及性能調優:iOS 視角》要點:
本文介紹了IM 技術在多應用場景下的實現及性能調優:iOS 視角,希望對您有用。如果有疑問,可以聯系我們。
本文基于作者在 MDCC 2016 移動開發者大會 iOS 開發峰會上的演講.
http://edu.csdn.net/course/detail/3175
https://github.com/MDCC2016/iOS-Session-Slides,歡迎 Star.
IM 已經成為當下 App 的必備模塊,在不同垂直領域,技術實現不盡相同.畢竟該如何選型?技術實現過程中,又該如何進行性能調優?本篇文章分為應用場景、技術實現細節、針對移動網絡特點的性能調優三個部分,具體講解IM即時通訊技術在社交、直播、紅包等不同場景下的技術實現與性能調優.
必要注意,本文中所涉及到的所有 iOS 相關代碼,均已 100% 開源(不存在 framework ),便于學習參考;本文側重移動端的設計與實現,會展開講,服務端僅僅屬于概述,不展開;本文還將為大家在設計或改造優化 IM 模塊,提供一些參考.
大規模即時通訊的技術難點
首先,思考幾個問題:
如安在移動網絡環境下優化電量、流量及長連接的健壯性?現在移動網絡有 2G、3G、4G 各種制式,且隨時可能切換和中斷,移動網絡優化可以說是面向移動服務的共同問題.
如何確保IM系統的整體平安?因為用戶的消息是個人隱私,所以要從多個層面來保證.
如何降低開發者集成門檻?
如何應對新iOS生態下的政策并結合新技術,好比 HTTP/2、IPv6、新的 APNs 協議等.
應用場景
IM 服務的最大價值在于什么?可復用的長連接.一切高實時性的場景,都適合使用 IM 來做,好比:視頻會議、聊天、私信、彈幕、抽獎、互動游戲、協同編輯、股票基金實時報價、體育實況更新;基于位置的應用(Uber、滴滴司機位置)、在線教育、智能家居等.
接下來,我們會挑一些典型場景進行介紹,并闡發其中具體的技術細節.
?IM 發展史
IM 基本的發展歷程是:輪詢、長輪詢、長連接.
下面挑選一些代表性的技術進行介紹:
一般的網絡哀求:一問一答,如圖1所示.
輪詢:頻繁的一問一答,見圖2.
長輪詢:耐心地一問一答,見圖3,曾被Facebook早期版本采用.
圖1 正常哀求
圖2 輪詢
圖3 長輪詢
一種輪詢方式是否為長輪詢,是根據服務端的處置方式來決定的,與客戶端沒有關系.
短輪詢很容易理解,那么,什么叫長輪詢?與短輪詢有何區別?長輪詢和短輪詢最大的區別是,短輪詢去服務端查詢時,不管服務端有沒有變化,服務器就立即返回結果了.而在長輪詢中,服務器如果檢測到庫存量沒有變化話,將會把當前哀求掛起一段時間(這個時間也叫作超時時間,一般是幾十秒).在這個時間內,服務器會檢測庫存量有沒有變化,變化就立即返回,否則就等到超時為止.
我們可以看到,發展歷史是這樣:從長短輪詢與長短連接,使用 WebSocket 來替代 HTTP.長短輪詢到長短連接的區別主要有:
概念范疇分歧:長短輪詢是應用層概念,長短連接是傳輸層概念;
協商方式不同:一個 TCP 連接是否為長連接,是通過設置 HTTP 的 Connection Header 來決定的,且必要兩邊都設置才有效.
實現方式不同:連接的長短是通過協議來規定和實現的.而輪詢的長短,是服務器通過編程的方式手動掛起哀求實現的.
在移動端上長連接是趨勢,其最大的特點是節省 Header.對比輪詢與 WebSocket 所花費的 Header 流量即可窺其一二.
假設 Header 是 871 字節,我們以相同的頻率 10W/s 去做網絡哀求,對比輪詢與 WebSocket 所花費的 Header 流量.其中,Header 包括哀求和響應頭信息.出于兼容性考慮,一般建立 WebSocket 連接也采用 HTTP 哀求的方式,從這個角度講,無論哀求如何頻繁,都只需要一個 Header.
而且,WebSocket 的數據傳輸是 Frame(幀)形式傳輸的,更加高效,對比輪詢的 2 個 Header,這里只有一個 Header 和一個 Frame.而 WebSocket 的 Frame 僅僅用 2 個字節就代替了輪詢的 871字節!
相同的每秒客戶端輪詢的次數,當次數高達 10W/s 的高頻率時,Polling 輪詢必要消耗 665Mbps,而 WebSocket 僅僅只花費了 1.526Mbps,將近 435 倍,如圖 4 所示.
圖4 輪詢與Websocket的花費的流量對比:435倍
數據參考:
HTML5 WebSocket: A Quantum Leap in Scalability for the Web
《微信、QQ 這類 IM App 怎么做 —— 談談 WebSocket》
接下來,探討下長連接實現方式的協議選擇.
?大家都在使用什么技術?
筆者最近做了兩個 IM 問卷,累計發生了 900 多條的投票數據,見圖5、圖6.
圖5 使用何種協議
圖6 使用協議投票成果
注:本數據只能反映出 IM 技術在 iOS 領域的使用情況,并不克不及反映出整個 IT 行業的情況.
下文會對投票結果進行下闡發.
協議如何選擇?
IM 協議選擇原則一般是:易于拓展,方便覆蓋各種業務邏輯,同時又比擬節約流量.后一點需求在移動端 IM 上尤其重要.常見的協議有:XMPP、SIP、MQTT、私有協議.我們這里只關注前三名,見表1.
一個好協議需要滿足如下條件:高效、簡潔、可讀性好、節約流量、易于拓展,同時又能匹配當前團隊的技術堆棧.基于以上原則,我們可以得出:如果團隊小,在 IM 上技術積累不夠可以考慮使用 XMPP 或者 MQTT+HTTP 短連接的實現.反之可以考慮本身設計和實現私有協議,這里建議團隊有計劃地遷移到私有協議上.
在此特別提一下排名第二的 WebSocket,區別于上面的聊天協議,這是一個傳輸通訊協議,那為什么會有這么多人在即時通訊領域運用了這一協議?除了上文說的長連接特性外,這個協議 Web 原生支持,有很多第三方語言實現,可以搭配 XMPP、MQTT 等多種聊天協議進行使用,被廣泛地應用于即時通訊領域.
社交場景
在當前社交場景下,最大的特點在于:模式成熟,界面類似.我們團隊專門為社交場景開發了一款開源組件—— ChatKit-OC,Star 數1000+.
ChatKit-OC 在協議選擇上使用的是 WebSocket 搭配私有聊天協議的方式,在數據傳輸上選擇的是 Protobuf(Google Protocol Buffer)搭配JSON的方式.
直播場景
在此分享一個演示如何為直播集成 IM 的開源直播 Demo:LiveKit-iOS.LiveKit 相較社交場景的特點有:無人數限制的聊天室、自定義消息、打賞機制的服務端配合.
數據自動更新場景
打車應用場景(Uber、滴滴等 App 移動小車);
朋友圈狀態的實時更新,朋友圈本身發送的消息無需刷新,自動更新.
這些場景比聊天要簡單許多,僅僅涉及到監聽對象的訂閱、撤消訂閱.正如上文所提到的,使用 MQTT 實現最為經濟.用社交類、直播類的思路來做,也可以實現,但略顯冗余.
電梯場景(假在線狀態處置)
iOS 端的假在線狀態有雙向 ping pong 機制和 iOS 端只走 APNs 兩種方案.此中,雙向 ping-pong 機制原理如下:
Message 在發送后,在服務端維護一個表,一段時間內,好比 15 秒內沒有收到 ack,就認為應用處于離線狀態,先將用戶踢下線,轉而進行推送.此處如果出現重復推送,客戶端要負責去重.將 Message 消息當作服務端發送的 Ping 消息,App 的 ack 作為 pong,如圖7所示.
圖7 雙向 ping-pong 機制原理
使用 APNs 來進行聊天的優缺點具體如下:
優點:辦理了 iOS 端假在線的問題.
缺點:(APNs的缺點)無法保證消息的及時性;讓服務端負載過重.
APNs 不保證消息的到達率,消息會被折疊,你可能見過圖8的推送消息.
圖8 消息推送折疊
這中間產生了什么?
當 APNs 向你發送了4條推送,但是你的設備網絡狀況不好,在 APNs 那里下線了.這時 APNs 到你的手機鏈路上有4條任務堆積,APNs 的處理方式是,只保存最后一條消息推送給你,然后告知推送數.那么其他三條消息呢?會被 APNs 丟棄.
有一些 App 的 IM 功能沒有維持長連接,是完全通過推送來實現的.通常情況下,這些 App 也已經考慮到這種丟推送的情況,它們的做法都是,每次收到推送后,向本身的服務器查詢當前用戶的未讀消息.但是 APNs 也同樣無法保證這四條推送能至少有一條到達你的 App.很遺憾地告訴這些 App,此次更新對你們所遭受的這些坑,沒有改善.
為什么這么設計?APNs 的存儲-轉發能力太弱,大量的消息存儲和轉發將消耗 Apple 服務器資源,可能是出于存儲本錢考慮,也可能是因為 Apple 轉發能力太弱.總之結果就是 APNs 從來不保證消息的達到率,并且設備上線之后也不會向服務器上傳信息.
現在我們可以保證消息必定能推送到 APNs 那里,但是 APNs 不保證幫我們把消息投遞給用戶.即使搭配了這樣的策略:每次收到推送就拉歷史記錄的消息,一旦消息被APNs丟棄,能會在幾天之后收到新推送后才被查詢到.
讓服務端負載過重:APNs的實現原理決定了必需每次收到消息后,拉取歷史消息.
結論:如果面向的目標用戶對消息的及時性并不敏感,可以采用這種方案,好比社交場景.
技術實現細節
?基于WebSocket的IM系統
WebSocket 是 HTML5 開始提供的一種瀏覽器與服務器間進行全雙工通訊的網絡技術.WebSocket 通信協議于 2011 年被 IETF 定為尺度 RFC 6455,WebSocket API 被 W3C 定為尺度.
在 WebSocket API 中,瀏覽器和服務器只必要做一個握手的動作,然后瀏覽器和服務器之間就形成了一條快速通道,由此兩者間就直接可以數據互相傳送.
只從 RFC 發布的時間看來,WebSocket 要晚些,HTTP 1.1 是 1999 年,WebSocket 則是 12 年之后了.WebSocket 協議的開篇就說,本協議的目的是為了解決基于瀏覽器的程序需要拉取資源時必須發起多個 HTTP 哀求和長時間的輪詢問題而創建.可以達到支持 iOS、Android、Web 三端同步的特性.
?針對移動網絡特點的性能調優
圖9 數據傳輸格式占比
圖10 數據傳輸格式投票成果
注:本次投票是發布在微博@iOS程序犭袁 ,鑒于微博關注機制,本數據只能反映出 IM 技術在 iOS 領域的使用情況,并不克不及反映出整個IT行業的情況.
?極簡協議,傳輸協議 Protobuf
使用 Protocol Buffer 減少 Payload,微信也同樣使用定制后的 Protobuf 協議.
攜程是采納新的 Protocol Buffer 數據格式 + Gzip 壓縮后的 Payload 大小降低了 15%-45%.數據序列化耗時下降了80%-90%.
采用高效平安的私有協議,支持長連接的復用,穩定省電省流量:
高效,提高網絡哀求成功率,消息體越大,失敗幾率越大.
流量消耗極少,省流量.一條消息數據用 Protobuf 序列化后的大小是 JSON 的 1/10、XML 格式的 1/20、二進制序列化的 1/10.同 XML 相比,Protobuf 性能優勢明顯.它以高效的二進制方式存
儲,比 XML 小3到10倍,快20到100倍.
省電
高效心跳包,同時心跳包協議對IM的電量和流量影響很大,對心跳包協議上進行了極簡設計:僅1Byte.
易于使用,開發人員通過依照一定的語法定義結構化的消息格式,然后送給命令行工具,工具將自動生成相關的類,可以支持 Java、C++、Python、Objective-C 等語言環境.通過將這些類包含在項目中,可以很輕松地調用相關方法來完成業務消息的序列化與反序列化工作.原生支持 C++、Java、Python、Objective-C 等多達10余種語言.2015年08月27日,Protocol Buffers v3.0.0-beta-1 中發布了 Objective-C(Alpha)版本,而在此后的兩個月前,Protocol Buffers v3.0.0 正式版發布,正式支持 Objective-C.
可靠,微信和手機QQ這樣的主流IM應用也早已在使用它.
如何測試:
對數據分別操作 100、1000、10000和100000進行了測試,如圖11所示,縱坐標是完成時間,單位是毫秒.
圖11 數據測試成果(來源:beyondbit博客)
圖12 thrift-protobuf-compare
圖12為 thrift-protobuf-compare,測試項為 Total Time,也就是指一個對象操作的整個時間,包含創建對象,將對象序列化為內存中的字節序列,然后再反序列化的整個過程.從測試結果可以看到Protobuf的成績很好.
缺點:不能表現復雜的數據結構,但 IM 服務已經足夠使用.
另外,可能會造成 App 的包體積增大,通過 Google 提供的腳本生成的 Model,會非常“龐大”,Model 一多,包體積也就會跟著變大.但在我們 SDK 中只使用了一個 Model,所以這個問題并不明顯.
?在平安上需要做哪些事情?
防止 DNS 污染
DNS 出問題的概率其實比大家感覺要大,首先是 DNS 被劫持或者失效,2015 年初業內比擬知名的就有 Apple 內部 DNS 問題導致 App Store、iTunes Connect 賬戶無法登錄;京東因為 CDN 域名付費問題導致服務停擺.
另一個常見問題就是 DNS 解析慢或者失敗,例如中國運營商網絡的 DNS 就很慢,一次 DNS 查詢甚至都能趕上一次連接的耗時,尤其 2G 網絡情況下,DNS 解析失敗是很常見的.因此如果直接使用 DNS,對于首次網絡服務哀求耗時和整體服務成功率都有非常大的影響.這一部分文章較長,單獨成篇,鏈接:http://geek.csdn.net/news/detail/111130.
賬戶平安
IM 服務賬號密碼一旦泄露,危害更加嚴峻,尤其是對于消息可以漫游的類型.在此,分享下我們的團隊是如何保障賬號平安的.
1. 帳號平安
無侵入的權限控制:與用戶的用戶帳號體系完全隔離,只需要提供一個 ID 就可以通信,接入方可以對該 ID 進行 MD5 加密后再進行傳輸和存儲,保證開發者用戶數據的私密性及平安.
2. 簽名機制
對關鍵操作,支持第三方服務器鑒權,保護你的信息平安.
3. 單點登錄
讓 App 支持單點登錄,能有限減少盜號造成的平安問題.在 ChatKit-OC 中,我們就默認開啟了單點登錄功能,以此來提升 App 的平安性.
重連機制
精簡心跳包,保證一個心跳包大小在10字節之內;
減少心跳次數:心跳包只在空閑時發送;從收到的最后一個指令包進行心跳包周期計時而不是固定時間.
重連冷卻2的指數級增長2、4、8,消息往來也算作心跳.類似于iPhone暗碼的錯誤機制,冷卻單位是5分鐘,10次輸錯,清除數據.
這樣靈活的策略也同樣決定了,只能在 App 層進行心跳 ping.
這里有需要提一下重連機制的需要性,我們知道 TCP 也有保活機制,但與我們在這里討論的“心跳保活”機制是有區別的,見表2.
圖13 保活,畢竟保的是誰?
比如:考慮一種情況,某臺服務器因為某些原因導致負載超高,CPU 100%,無法響應任何業務哀求,但是使用 TCP 探針則仍舊能夠確定連接狀態,這就是典型的連接活著但業務提供方已死的狀態.對客戶端而言,這時的最好選擇就是斷線后重新連接其他服務器,而不是一直認為當前服務器是可用狀態,總是向當前服務器發送些必然會失敗的哀求.
?使用HTTP/2減少不需要的網絡連接
大多數移動網絡(3G)并不允許一個給定 IP 地址超過兩個并發 HTTP 哀求,即當你有兩個針對同一個地址的連接時,再發起的第三個連接總會超時.而2G網絡下這個限定是1個,同一時間發起過多的網絡哀求不僅不會起到加速的效果,反而有副作用.
另一方面,由于網絡連接很是費時,保持和共享某一條連接就是一個不錯的選擇,比如短時間內多次的 HTTP 哀求,使用 HTTP/2 就可以達到這樣的目的.
HTTP/2 是HTTP協議發布后的首個更新,于2015年2月17日被批準.它采納了一系列優化技術來整體提升 HTTP 協議的傳輸性能,如異步連接復用、頭壓縮等等,可謂是當前互聯網應用開發中,網絡層次架構優化的首選方案之一.
HTTP/2 也以高復用著稱,而且如果我們要使用 HTTP/2,那么在網絡庫的選擇上必然要使用 NSURLSession,所以 AFN2.x 也必要升級到 AFN3.x.
設置合理的超時時間
過短的超時容易導致連接超時的事情頻頻發生,甚至一直無法連接,而過長的超時則會帶來等待時間過長、體驗差的問題.就目前來看,對于普通的 TCP 連接,30秒是個不錯的值,而 HTTP 哀求可以按照重要性和當前網絡情況動態調整,盡量將超時控制在一個合理的數值內,以提高單位時間內網絡的利用率.
圖片視頻等文件上傳
圖片格式優化在業界已有成熟的方案,例如 Facebook 使用的 WebP 圖片格式,已經被國內眾多 App 使用.分片上傳、斷點續傳、秒傳技術.
文件分塊上傳:因為移動網絡丟包嚴重,將文件分塊上傳可以使得一個分組包括合理數量的 TCP 包,使得重試概率下降,重試代價變小,更容易上傳到服務器;
提供文件秒傳的方式:服務器根據 MD5、SHA 進行文件去重;
支持斷點續傳;
上傳失敗,合理的重連,好比3次.
使用緩存
微信不用考慮消息同步問題,因為它是不存儲歷史記錄的,卸載重裝消息記錄就會丟失.所以我們可以采用一個類似 E-Tag、Last-Modified 的本地消息緩存校驗機制.具體做法是,當我們想加載最近10條聊天記錄時,先將本地緩存的最近10條做一個 hash 值,將其發送給服務端,服務端將最近十條做 hash,如果一致就返回304.最抱負的情況是服務端一直返回304,一直加載本地記錄,這樣做的好處是消息同步、節省流量.
作者簡介: iOS 開發工程師,現任職于 LeanCloud,熱愛開源與分享,GitHub 獲得的 Star 數過萬,也是多個開源項目的維護者,好比 CYLTabBarController、ChatKit 等.
了解最新移動開發相關信息和技術,請存眷 mobilehub 公眾微信號(ID: mobilehub).
《IM 技術在多應用場景下的實現及性能調優:iOS 視角》是否對您有啟發,歡迎查看更多與《IM 技術在多應用場景下的實現及性能調優:iOS 視角》相關教程,學精學透。維易PHP學院為您提供精彩教程。