Android微信智慧心跳方案【轉】
前言:在13年11月中旬時,因為基礎元件組人手緊張,Leo安排我和春哥去廣州輪崗支援。剛到廣州的時候,Ray讓我和春哥對Line和WhatsApp的心跳機制進行分析。我和春哥抓包測試了差不多兩個多禮拜,在我們基本上摸清了Line和WhatsApp的心跳機制後,Ray才告訴我們真正的任務——對微信的固定心跳進行優化,並告訴我們這不是一件容易的事情。於是我和春哥開始構思第一個方案,我們開始想用統計的方法來解決問題,當我們拿著第一個方案和Ray討論時,發現不能優雅應對Ray的所有提問:1、測試環境的準確性,失敗到底是因為網路的特性導致還是因為使用者當前的環境變化導致的暫時失敗。2、臨界值界定,如果方案選中的心跳值是臨界值,我們該怎麼辦。Ray和元件組同事在網路方面有極其豐富的經驗,雖然他沒有給我們指出明確的方向,但提出的問題幫助我們更快的補齊需要面對的核心問題。這兩個問題讓我和春哥意識到如果能很好的解決,就可以給出一個比較好的心跳方案。第一個問題我和春哥開始就意識到,第二個問題我們確實在一開始時疏忽了。但直接解決這兩個問題確實不容易,這著實讓我和春哥迷茫了幾天,有兩三天在紡園我都沒怎麼睡著,因為想不到更好的方法。直到有一天思路發生了一些轉變,既然最優解比較複雜,為什麼不繞過去,使用有損服務理念找次優解呢。讓複雜的事情簡單化,好了,想到這裡突然有一種撥開雲霧的感覺。
思路對了,方案就可以做到簡單並且可靠,大家可以看到最終的方案是比較簡單的,並且效果還挺好的。在方案描述之前大概講一下減低問題複雜度的方法:
a)延遲心跳測試法:這是測試結果準確的前提保障,我們認為長連線建立後連續三次成功的短心跳就可以很大程度的保證下一次心跳環境是正常的。
b)成功一次認定,失敗連續累積認定:成功是絕對的,連續失敗多次才可能是失敗。
c)臨界值避免:我們使用比計算出的心跳稍微小一點的值做為穩定心跳避免臨界值。
d)動態調整:即使在一次完整的智慧心跳計算過程中,我們沒有找到最好的值,我們還有機會來進行校正。
當我和春哥想出第二個簡單易行的方案後,我們心裡就很有底了,去找Ray討論,Ray聽完後一次通過,然後Ray約了Harvey,給Harvey講完後,Harvey說聽起來可以,可以試試。
然後就開始動手,分析競品加確定方案花了差不多兩個月。寫心跳的主要程式碼,只花了一天時間,我記得那天是年會後的一天。回過頭來再看這個方案花費的時間還是值得的,後來灰度的統計資料顯示,70%使用者都可以達到我們的心跳上限。
搞完智慧心跳後一段時間在廣州沒事幹,我就跟Ray商量,Ray讓我去測試下WebView的效能瓶頸。然後我跟周斯基一起來做這件事,搞完了安卓客戶端WebView效能瓶頸測試後,因為懷孕的老婆一個人在深圳,領導就安排我先回深圳了。春哥堅守著把GCM部分完成後才回深圳。
等我們的心跳版本正式釋出後,一年前我在公司km上分享了智慧心跳方案,吸引不少做push的同事加入了討論,感覺這方面的交流還是很有必要的。
好了,廢話了很多,下面分享一下微信的智慧心跳方案細節。由於字數比較多,建議大家使用PC版微信檢視。
1.主要目標
本方案的主要目標是,在儘量不影響使用者收訊息及時性的前提下,根據網路型別自適應的找出保活信令TCP連線的儘可能大的心跳間隔,從而達到減少安卓微信因心跳引起的空中通道資源消耗,減少心跳Server的負載,以及減少部分因心跳引起的耗電。
主要方法是參考WhatsApp和Line中有價值的做法,結合影響TCP連線壽命的因素,實現Android微信後臺自適應心跳演算法,同時使用GCM作為輔助通道增加新訊息通知的可靠性。
2. WhatsApp、Line、微信的Push策略分析
2.1 WhatsApp
在不支援GCM的裝置上,採用和微信類似的長連線+心跳策略,WIFI和手機網路下的心跳間隔都為4分45秒,心跳5次後,主動斷開連線再重連。
在支援GCM的裝置上,主要靠GCM來啟用WhatsApp,WhatsApp啟動後,會建立一個與伺服器的長連線,直接通過此長連線傳送Push訊息,這個長連線10分鐘無訊息就會主動斷掉,且這十分鐘內不做心跳,斷掉後WhatsApp客戶端和它的伺服器不再有連線。當有訊息時候,伺服器發現沒有長連線會發送GCM訊息,手機收到GCM訊息後,會重新建立長連線來收取訊息,10分鐘無訊息會再斷開,如此迴圈。
2.2 Line
從測試中發現Line在國內、臺灣、美國使用了不同的策略。
1、美國(使用GCM):
啟動時,會保持7分鐘心跳(CDMA2000網路)維持長連線半小時,之後主動斷開長連線。當有訊息時,伺服器會發送GCM訊息,Line客戶端接收到GCM訊息後,重新建立長連線,並再次用心跳維持半個小時。
2、國內(不使用GCM):
在國內,同樣帳號在相同網路,不同的手機上測出了兩種策略:
長連線+心跳策略(在Galaxy S3上使用),心跳間隔WIFI下是3分20秒,手機網路是7分鐘。
輪詢策略(在紅米和Nexus S上使用),如圖2-1所示。與心跳策略的主要區別用紅色標出,客戶端在長連線建立後也會定時傳送請求,Server會回覆並且同時關閉長連線。客戶端等待輪詢間隔T1後再次建立TCP連線。Line會根據手機的活躍狀態動態調整T1,調整範圍是從最小1分到最大到2小時半。而長連線存活時間T2比較固定,在WIFI下4分鐘,手機網路7分鐘。如果在T2時收到新訊息會延長T2的時間。
圖2-1 Line在國內的輪詢策略
3、臺灣(不使用GCM):
從IBG同事win和guang提供的測試資料中看到,臺灣使用的策略跟國內的輪詢策略類似。
2.3 微信
微信沒有使用GCM,自己維護TCP長連線,使用固定心跳。
2.4心跳典型值
|
Line |
GCM |
|
WIFI |
4分45秒 |
3分20秒 |
15分鐘 |
手機網路 |
4分45秒 |
7分鐘 |
28分鐘 |
2.5Line、WhatsApp、微信Push策略的優點
a)微信:當前心跳間隔比競品短,所以微信在新訊息提醒上會最及時。
b)使用GCM:Line和WhatsApp使用GCM策略的最大優點就是省電,以及減輕系統負荷(減少後臺應用數目)。
c)Line:Line的輪詢策略,優點是當Line處於活躍狀態時,及時收訊息。當Line處於不活躍狀態時,省電。
2.6Line、WhatsApp微信Push策略的不足
a)微信當前心跳頻率相對競品較大,在耗電、耗流量,佔用信令通道等方面有所影響。
b)Line的輪詢策略,導致的問題是訊息可能會延遲接收,測試發現最大延遲間隔到2.5小時。
c)WhatsApp和Line使用Push拉起一個定時長連線策略,缺點是要依賴Google的Push服務,如果Google的Push服務不穩定,訊息也會延遲接收。
d)在國內的移動和聯通2G網路下,由於運營商的策略,GCM長連線頻繁斷連,WhatsApp的Push訊息很不及時,體驗非常差。
3. GCM研究
3.1 GCM特點
a)Android2.2以下的手機不支援GCM,2.2到3.0需要安裝Google Store並設定Google帳號,4.04及以上版本不需要設定帳號也能支援。
b)GCM只傳遞資料(可以傳遞小於4kb的資料),對這些資料的處理可以全部由開發者控制。
c)Android應用不需要執行就可以接收訊息(通過Android廣播)。
d)GCM不保證傳送的訊息的順序,也不保證訊息一定能夠推送到手機。
3.2 GCM心跳策略以及存在的問題
a)用心跳保活長連線,心跳間隔為WIFI下15分鐘,資料網路下28分鐘。
b)Google可以改變所有Android裝置的心跳間隔值(目前還未改變過)。
c)GCM由於心跳間隔固定,並且較長,所以在NAT aging-time設定較小的網路(如聯通2G,或有些WIFI環境下)會導致TCP長連線在下一次心跳前被閘道器釋放。造成Push延遲接收。
3.3 GCM的可用性及穩定性
目前測試發現GCM在國內可用性不高,原因有:
a) Android很多被手機廠商定製化,廠商可能會去掉GCM服務。
b) Android2.2到3.0之間需要安裝Google Store並設定Google帳號。
c)由於國內2G和移動3G的NAT超時時間都小於GCM心跳時間(28分鐘),TCP長連線必然無法保活,每次都要等28分鐘心跳失敗重連後才能收到Push。
d)某些運營商可能限制了5228埠,移動3G/2G下,發現幾乎無法連線上GCM伺服器,也就無法獲得GCM通知,WhatsApp放後臺10分鐘後,經常很長時間都收不到Push訊息。
在美國3G網路下抓包的24小時,GCM的連線極其穩定,24小時內GCM長連線未曾斷過,在臺灣3G網路下抓包14個小時,GCM連線也只斷過一次。WhatsApp使用者在此類地區網路下客戶端可以獲得很及時的Push通知。
在中國電信3G下抓包,大部分時間GCM連線都比較穩定,只會因為偶爾的DHCP造成斷連現象,由於頻率很低(平均數小時才發生一次),對Push體驗的影響不大。
3.4 GCM Server型別
GCM提供兩種Server模型:
a)HTTP Server : 使用同步介面傳送HTTP請求,一次請求可以發給最多1000個裝置。
b)XMPP Server :使用非同步介面傳送請求,只支援對單個裝置(或同一個使用者的多個關聯裝置傳送),傳送請求併發數須小於1000,支援裝置到雲端Server傳送資料。需要Google將我們的傳送Server加入白名單。
4.微信可能的改進點探討
微信Push的優化主要有幾個優化點:
a) 公共Push通道
b) 使用GCM Push作為輔助通道
c)自適應心跳間隔優化
4.1 公共Push通道
由於GCM在國內的可靠性很低,現在國內Android上的Push基本上是各自為政,很多軟體都自己實現Push。導致手機被經常性的喚醒,耗電耗流量嚴重。
市面上已經有很多第三方的公共推送服務,大家可以選擇一個適合自己應用的推送服務。騰訊也有信鴿和維納斯元件,大家在選擇方案的時候可以對比下。
最終因為我們國內外使用一套方案,並且是輔助公道,所以我們選擇使用GCM。
4.2 使用GCM Push作為輔助通道
當前使用GCM的成本不大,可以使用GCM作為輔助通道來增加新訊息的及時性。
使用GCM作為輔助通道,在支援GCM的裝置上微信上傳自己的註冊GCM ID給微信Server。
微信Server在發現長連線失效的情況下,可以使用GCM 作為輔助通道通知客戶端有新訊息,客戶端收到push通知後做一次sync。
只利用GCM來啟用微信,不傳遞訊息的具體資料,要控制給同一裝置傳送GCM通知的時間間隔(如五分鐘)。
4.3 自適應心跳間隔優化
4.3.1影響TCP連線壽命的因素
在Android下,不管是GCM,還是微信,都是通過TCP長連線來進行Push訊息的,TCP長連線存活,訊息Push就及時,所以要對影響TCP連線壽命的因素進行研究。
1、NAT超時
大部分移動無線網路運營商都在鏈路一段時間沒有資料通訊時,會淘汰 NAT 表中的對應項,造成鏈路中斷(NAT超時的更多描述見附錄6.1)。NAT超時是影響TCP連線壽命的一個重要因素(尤其是國內),所以客戶端自動測算NAT超時時間,來動態調整心跳間隔,是一個重要的優化點。
2、DHCP的租期(lease time)
目前測試發現安卓系統對DHCP的處理有Bug,DHCP租期到了不會主動續約並且會繼續使用過期IP,這個問題會造成TCP長連線偶然的斷連。(租期問題的具體描述見附錄6.2)。
3、網路狀態變化
手機網路和WIFI網路切換、網路斷開和連上等情況有網路狀態的變化,也會使長連線變為無效連線,需要監聽響應的網路狀態變化事件,重新建立Push長連線。
4.3.2 心跳範圍選擇
1、前後臺區分處理:
為了保證微信收訊息及時性的體驗,當微信處於前臺活躍狀態時,使用固定心跳。
微信進入後臺(或者前臺關屏)時,先用幾次最小心跳維持長連結。然後進入後臺自適應心跳計算。這樣做的目的是儘量選擇使用者不活躍的時間段,來減少心跳計算可能產生的訊息不及時收取影響。
2、後臺自適應心跳選擇區間:
可根據自身產品的特點選擇合適的心跳範圍。
4.3.3 狀態轉換圖
4.3.4自適應心跳演算法描述
1、按網路型別區分計算:
因為每個網路的NAT時間可能不一致。所以需要區分計算,資料網路按subType做關鍵字,WIFI按WIFI名做關鍵字。
對穩定的網路,因為NAT老化時間的存在,在自適應計算態的時候,暫設計以下步驟在當前心跳區間逼近出最大可用的心跳。
a) 變數說明:
[MinHeart,MaxHeart]——心跳可選區間。
successHeart——當前成功心跳,初始為MinHeart
curHeart——當前心跳初始值為successHeart
heartStep——心跳增加步長
successStep——穩定期後的探測步長
b) 最大值探測步驟:
圖4-1 自適應心跳計算流程
自適應心跳計算流程如圖4-1所示,經過該流程,會找到必然使心跳失敗的curHeart(或者MaxHeart),為了保險起見,我們選擇比前一個成功值稍微小一點的值作為後臺穩定期的心跳間隔。
影響手機網路測試的因素太多,為了儘量保證測試結果的可靠性,我們使用延遲心跳測試法。在我們重新建立TCP連線後,先使用 短心跳連續成功三次,我們才認為網路相對穩定,可以使用curHeart進行一次心跳測試。圖4-2顯示了一次有效心跳測試過程。圖4-3顯示了在沒有達到穩定網路環境時,我們會一直使用固定短心跳直到滿足三次連續短心跳成功。
使用延遲心跳測試的好處是,可以剔除偶然失敗,和網路變化較大的情況(如地鐵),使測試結果相對可靠(五次延遲測試確定結論)。同時在網路波動較大的情況,使用短心跳,保證收取訊息相對及時。
c) 執行時的動態調整策略(已經按測算心跳穩定值後)
NAT超時值算出來後,在維持心跳的過程中的策略
ü 無網路、網路時好時壞、偶然失敗、NAT超時變小:在後臺穩定期發生心跳發生失敗後,我們使用延遲心跳測試法測試五次。如果有一次成功,則保持當前心跳值不變;如果五次測試全失敗,重新計算合理心跳值。該過程如圖4-4所示,有一點需要注意,每個新建的長連線需要先用短心跳成功維持3次後才用successHeart進行心跳。
圖4-2 後臺穩定態動態調整心跳策略
ü NAT超時變大:以周為週期,每週三將後臺穩定態調至自適應計算態,使用心跳延遲法往後探測心跳間隔。
ü successHeart是NAT超時臨界值:因為我們現在選擇的是一個比successHeart稍小的值作為穩定值,所以在計算過程中可以避開臨界值。當運營商在我們後臺穩定期將NAT超時調整為我們當前計算值,那麼由於我們每週會去向下探索,所以下一週探測時也可以及時調整正確。
4.3.5 冗餘Sync和心跳
在使用者的一些主動操作以及聯網狀態改變時,增加冗餘Sync和心跳,確保及時收到訊息。
1、當用戶點亮螢幕的時候,做一次心跳。
2、當微信切換到前臺時,做一次Sync。
3、聯網時重建信令TCP,做一次Sync
5. 可能存在的風險及預防措施
5.1 DHCP租期因素
1、問題:根據目前的測試結果顯示,安卓不續約到期的IP Bug,會導致TCP連線在不確定的時間點失效,從而會導致一次心跳失敗。
2、預防:統計後臺穩定期的心跳成功率,上報給後臺。後臺可以按地區分網路監控這個指標的波動,並且後臺可以根據不同的波動,動態調整某區域特定網路下可選的心跳區間。
5.2 其他影響TCP壽命的因素
是否有遺漏的因素?歡迎各位聯絡我反饋。
6 附錄
6.1 附錄A——NAT超時介紹
因為 IP v4 的 IP 量有限,運營商分配給手機終端的 IP 是運營商內網的 IP,手機要連線 Internet,就需要通過運營商的閘道器做一個網路地址轉換(Network Address Translation,NAT)。簡單的說運營商的閘道器需要維護一個外網 IP、埠到內網 IP、埠的對應關係,以確保內網的手機可以跟 Internet 的伺服器通訊。
NAT 功能由圖中的 GGSN 模組實現
大部分移動無線網路運營商都在鏈路一段時間沒有資料通訊時,會淘汰 NAT 表中的對應項,造成鏈路中斷。下表列出一些已測試過的網路的NAT超時時間(更多資料由於測試條件所限沒有測到):
地區/網路 |
NAT超時時間 |
中國移動3G和2G |
5分鐘 |
中國聯通2G |
5分鐘 |
中國電信3G |
大於28分鐘 |
美國3G |
大於28分鐘 |
臺灣3G |
大於28分鐘 |
長連線心跳間隔必須要小於NAT超時時間(aging-time),如果超過aging-time不做心跳,TCP長連線鏈路就會中斷,Server就無法傳送Push給手機,只能等到客戶端下次心跳失敗後,重建連線才能取到訊息。
6.2 附錄B——安卓DHCP的租期(lease time)問題
目前測試發現安卓系統對DHCP的處理有Bug:
1、 DHCP租期到了不會主動續約並且會繼續使用過期IP,詳細描述見http://www.net.princeton.edu/android/android-stops-renewing-lease-keeps-using-IP-address-11236.html。這個問題導致的問題表象是,在超過租期的某個時間點(沒有規律)會導致IP過期,老的TCP連線不能正常收發資料。並且系統沒有網路變化事件,只有等應用判斷主動建立新的TCP連線才引起安卓裝置重新向DHCP Server申請IP租用。
2、 未到租期的一半時間,安卓裝置重新向DHCP Server申請IP租用。從目前測試結果來看,這種現象恢復的比較快。
3、 移動2G/3G,聯通2G沒有抓到DHCP。
4、 美國3G下抓取24小時,沒有抓到DHCP。