《億級Android架構》小專欄文章列表:html
《Android 架構之長鏈接技術》android
《Android 架構之網絡安全演進》github
《Android 架構之高性能移動端日誌系統》golang
上一篇文章《Android 架構之網絡框架(上)》中,咱們談過了網絡框架OkHttp、網絡加速方案如HttpDNS、數據壓縮與序列化等技術點。本文咱們結合騰訊Mars框架和美團Shark體系等業內主流長鏈接方案,談一談長鏈接技術的各個方面。後端
本文會包括下面的技術點:安全
除了你們經常使用的Http短鏈接,大型App幾乎都會搭建一套完整的TCP長鏈接
網絡通道。咱們先來看下美團Shark長鏈接
的線上數據:
圖片來源 《美團點評移動網絡優化實踐》
上面兩張圖片對比了長/短鏈接的成功率和網絡延時數據,這兩個是網絡模塊最重要的衡量指標。能夠看出,不管是成功率,仍是網絡延時,長鏈接都明顯優於短鏈接。
另外,你們都知道微信的消息收發很是即時,這便歸功於背後穩定高可用的長鏈接系統。實際上,微信除了消息收發,其餘的小數據通訊都是經過長鏈接來實現的。
下面咱們來說解一些長鏈接的一些核心技術點。
爲防止你們對於長鏈接和短鏈接混淆,這裏先簡單說明下幾點區別。
這二者分別對應的是TCP協議層
的長鏈接
和短鏈接
。
你們都知道,TCP會經過三次握手,創建與服務端的鏈接,而後傳遞數據,只不過短鏈接
在數據傳輸完後,會主動關閉鏈接,而長鏈接
會繼續保持這條鏈接,後續的數據讀寫繼續使用這條鏈接。
Keep-Alive
上一篇文章中提到了鏈接複用
,經過Http1.1的Keep Alive
字段,咱們可讓一條Http鏈接保持不被當即關閉。有些同窗這時就疑惑了,是否是長鏈接就是Keep Alive
呢?
其實不是的。長鏈接咱們也叫TCP長鏈接
,它是架設在TCP協議上的,而上面說的Keep Alive
是Http協議的內容,連協議都不一樣,二者天然不是一個東西。
開啓了Keep Alive
是Http鏈接,咱們也稱之爲持久鏈接
,和長鏈接並不一樣。感興趣可參考此文:《TCP 進階》。
Keep-Alive
vs Http的Keep-Alive
提到Keep Alive
,有些同窗就會問了,TCP協議裏也有一個Keep Alive
,它和Http協議裏的Keep Alive
有什麼區別嗎?
兩者的用處並不一樣。Http協議在完成一個請求後,服務器會自動關閉鏈接。這時,能夠在請求裏帶上一個Keep Alive
給服務器,告訴服務器不要當即關閉鏈接
,我還想繼續複用這條鏈接;而對TCP協議層而言,是不會自動斷開的,但這也帶來了一個問題,萬一因爲某些外部緣由致使鏈接斷開,那我如何知道鏈接已失效呢?TCP會在2個小時間隔後,自動發送一個Keep Alive
數據包給服務端,探測一下服務器是否還在響應。它的功能相似心跳包,只是間隔太長,不適合作真正的心跳包。
那麼,相比Http短鏈接,長鏈接技術能帶來什麼好處呢?
之前咱們不一樣域名的請求,須要作對應的DNS請求,而後創建對應的Http鏈接。上篇文章裏說的Http鏈接池
在不一樣域名下不可複用,須要從新創建鏈接。這些都是一些資源開銷,可是若是經過長鏈接通道,那域名只是這個請求裏的一個字段,能夠直接複用同一條長鏈接通道。
上文中咱們提到了HttpDNS
,雖然它比系統DNS更優,但終歸仍是要作DNS操做。而長鏈接都是IP直接鏈接,所以沒有DNS相關的開銷和耗時。
對於大型App而言,存在繁多密集的網絡請求,這中間就會存在很是屢次的Http斷開和從新鏈接,浪費了不少時間和帶寬。而經過長鏈接通道的話,則沒有這部分耗時,直接傳輸二進制數據便可,節省了每次鏈接裏Header之類的帶寬開銷。
對於上面提到的微信消息接收等場景,若是須要客戶端主動去輪詢,則會頻繁發起請求,對於服務器會產生很大的負載壓力,浪費帶寬流量。而經過長鏈接,服務端能夠主動把消息下發給客戶端,作到最高實時性,且節省流量。
正常而言,長鏈接是不會斷開的。你們能夠本身試一試,兩個socket創建鏈接,只要網絡不變、一切正常,那麼這兩個socket能夠一直互相傳送數據,不會斷開。
可是,在移動網絡下,網絡狀態複雜多變,好比網絡線路被切斷、服務器宕機等,都會致使長鏈接中斷。除了這些線路異常外,咱們須要關注下面幾個長鏈接斷開緣由:
這個很容易理解,若是咱們的App切換到後臺,那麼系統隨時可能將咱們的App殺掉,這時長鏈接天然也就隨之斷開。
好比手機網絡斷開,或者發生Wi-Fi和蜂窩數據切換,這時會致使手機IP地址變動。而咱們知道,TCP鏈接是基於IP + Port的,一旦IP變動,TCP鏈接天然也就失效了,或者說長鏈接也就至關於斷開了。
這裏對NAT簡單解釋下,方便有的同窗不太瞭解。當手機鏈接上網絡時,網關會給咱們分配一個IP地址,這個實際上是內網IP,此時還未真正鏈接上公網,也鏈接不上服務器;若是想要鏈接公網,須要運營商將咱們的內網IP映射成一個公網IP,有了公網IP,服務器就能與咱們創建鏈接了。NAT指的就是這個映射過程。
也就是說,運營商會給每臺設備分配一個公網IP,相似一張通訊證。不過,隨着鏈接網絡的設備不斷增多,網關負載也會不斷加大,這時,運營商就會對一些不太活躍的設備進行公網IP回收了,若是下次這個設備須要連網,那就從新分配一個IP便可。
看似沒問題,但實際上,若是咱們的App在一段時間不活躍,發生了NAT超時,便會致使咱們的公網IP失效,長鏈接也隨之失效了。
DHCP 租期過時,若是沒有及時續約,一樣會致使IP地址失效。
綜合而言,長鏈接在正常狀況下是不會斷開的,可是,一旦手機的IP地址失效,這時就不得不從新創建鏈接了。
上面咱們提到了多種長鏈接斷開的緣由,那咱們應該如何進行優化,儘量保證長鏈接不斷開,或者及時斷開了,也要儘快重連呢?
爲了減小進程被殺的概率,在Mars的Demo代碼裏咱們能夠看到,它將長鏈接邏輯單獨提取到了一個獨立的進程裏。這個進程只作網絡交互,消耗的內存等資源天然較少,從而減小了被系統回收的機率。
進程被殺難以免,不過能夠經過AlarmReceiver、 ConnectReceiver、BootReceiver,達到進程的及時喚醒。
固然,進程保活是一個比較大的話題,並且不恰當的進程保活也會對系統體驗形成危害。這裏就不深究了。
對於心跳包不少人誤覺得只是用來按期告訴服務端咱們的狀態,實際並不是如此。
上面咱們提到了 NAT 超時,即若是App一段時間內不活躍,會致使運營商那裏刪除咱們的公網IP映射關係,這會致使咱們的TCP長鏈接斷開。所以,咱們須要經過心跳機制來保證App的活躍度,防止發生 NAT 超時。
在線上運行時,長鏈接頗有可能會因爲網絡切換之類的緣由斷開。這時,咱們須要儘快發現
長鏈接斷開,並當即重連
。通常有下面幾種作法:
上面咱們說了,心跳機制主要是爲了防止 NAT 超時,外網IP地址失效。所以,通常的作法就是在NAT失效前,保證有心跳包發出。或者說,客戶端應當以略小於NAT超時時間的間隔來發送心跳包。
早期的微信的心跳是4.5分鐘發送一次心跳,能夠不錯的運行。
在儘可能不影響用戶收消息及時性的前提下,根據網絡類型自適應的找出保活信令TCP鏈接的儘量大的心跳間隔,從而達到減小安卓微信因心跳引發的空中信道資源消耗,減小心跳Server的負載,以及減小部分因心跳引發的耗電。
所以,在固定心跳機制下,微信又研究了一套動態計算心跳的方案,動態的探測最大的NAT超時時間,而後選定合適的心跳間隔區間去發送心跳包。這裏說一下大體思路:
首先,若是心跳間隔越久,產生的負載和消耗也會越小。所以微信採用了自適應心跳
:當找到一個有效心跳間隔後,咱們主動去加大這個間隔,而後測試是否能成功,若是不能,則使用比上一次成功間隔稍短的時間做爲間隔;不然繼續加大間隔,直到找到可用的有效間隔。
那麼,如何判斷一個心跳間隔有效呢?微信採用的方案是使用固定短心跳直到知足三次連續短心跳成功,則認爲這個間隔有效。
探測過程大體爲:60秒短心跳,連續發3次後開始探測,90,120,150,180,210,240,270
另外,考慮到App在先後臺對於長鏈接的需求是不一樣的。所以當微信在前臺活躍態時,採用了固定心跳
機制;在前臺熄屏態或者後臺活躍態(進入後臺10分鐘內)時,先用幾回最當心跳維持長鏈接,而後進入自適應心跳
機制;在後臺穩定態(超過10分鐘),則採用自適應心跳計算出來的最大心跳做爲固定值。
若是在運行過程當中,發生了心跳失敗,則進行重連。同時將心跳間隔調整爲斷線前間隔減去20s,從新走自適應心跳;若是連續5次均失敗,則以初始心跳180s繼續測試。
對於Android系統而言,爲了減小頻繁喚醒系統致使的電量損耗,提供了Alarm對齊喚醒
機制:把必定時間段內的屢次Alarm喚醒合併成一次,減小系統被喚醒次數,增長待機時間。
而咱們的心跳包就是須要在定時結束後自動觸發一次心跳包的發送,所以,在Mars裏面的心跳時間也是按照Alarm對齊時間來作心跳間隔,減小電量損耗。
對於微信心跳策略感興趣的話能夠閱讀文末的參考文獻,代碼能夠參考smart_heartbeat。
長鏈接傳遞的是二進制數據,先後端能夠自行協商每一個字節要存放的內容便可。固然,也能夠考慮採用一些通用協議:好比SMTP、ProtoBuf等序列化方案。
參考文章:《一個基於TCP/WebSockets的超級精簡的長鏈接消息協議》.
另外,在數據加密方面,能夠結合非對稱加密算法RSA和對稱加密算法AES來對數據進行加密傳輸。
這一點不是本文的重點,不作過多贅述。
上面講了長鏈接的優點,那咱們該如何搭建整個長鏈接通道呢?這裏咱們以美團的長鏈接通道爲例子進行說明,各大廠的方案也是相似的。
本文結合了國內大廠如騰訊、美團等長鏈接框架,針對長鏈接這個技術點作了完整的介紹和剖析,若有不對或疑問,歡迎留言。
謝謝。
wingjay
業務的快速增加離不開穩定可靠的架構。《億級Android架構》小專欄會基於做者實際工做經驗,結合國內大廠如阿里、騰訊、美團等基礎架構現狀,嘗試談談如何設計一套好的架構來支持業務從0到1,甚至到億,但願與你們多多探討。
本專欄主要內容:
《億級Android架構》小專欄文章列表:
《Android端消息推送總結:實現原理、心跳保活、遇到的問題等》
公衆號,專一於Android、Java、大前端等技術領域,也包含程序猿成長、跳槽等內容。
![]()