Android推送 智慧心跳解決方案 手機休眠對心跳的影響
轉載請標明出處:http://blog.csdn.net/xx326664162/article/details/51611625 文章出自:薛瑄的部落格
你也可以檢視我的其他同類文章,也會讓你有一定的收貨!
Android推送服務的幾種實現方式
一、推送方式基礎知識:
在移動網際網路時代以前的手機,如果有事情發生需要通知使用者,則會有一個視窗彈出,將告訴使用者正在發生什麼事情。可能是未接電話的提示,日曆的提醒,或是一封新的彩信。推送功能最早是被用於Email中,用來提示我們新的資訊。由於時代的發展和移動網際網路的熱潮,推送功能更加地普及,已經不再僅僅用在推送郵件了,更多地用在我們的APP中了。
當我們開發需要和伺服器互動的應用程式時,基本上都需要獲取伺服器端的資料,比如《地震應急通》就需要及時獲取伺服器上最新的地震資訊。
要獲取伺服器上不定時更新的資訊,一般來說有兩種方法:
第一種是客戶端使用Pull(拉)的方式,就是隔一段時間就去伺服器上獲取一下資訊,看是否有更新的資訊出現。
第二種就是 伺服器使用Push(推送)的方式,當伺服器端有新資訊了,則把最新的資訊Push到客戶端上。這樣,客戶端就能自動的接收到訊息。
雖然Pull和Push兩種方式都能實現獲取伺服器端更新資訊的功能,但是明顯來說Push方式比Pull方式更優越。因為Pull方式更費客戶端的網路流量,更主要的是費電量,還需要我們的程式不停地去監測服務端的變化。
在開發Android和iPhone應用程式時,我們往往需要從伺服器不定的向手機客戶端即時推送各種通知訊息。我們只需要在Android或IPhone的通知欄處向下一拉,就展開了Notification Panel,可以集中一覽各種各樣通知訊息。目前IOS平臺上已經有了比較簡單的和完美的推送通知解決方案,我會在以後詳細介紹IPhone中的解決方案,可是Android平臺上實現起來卻相對比較麻煩。
二、三種常見的解決方案實現原理:
1)輪詢(Pull)方式:應用程式應當階段性的與伺服器進行連線並查詢是否有新的訊息到達,你必須自己實現與伺服器之間的通訊,例如訊息排隊等。而且你還要考慮輪詢的頻率,如果太慢可能導致某些訊息的延遲,如果太快,則會大量消耗網路頻寬和電池。
但對於即時通訊產品來說, 這種方案完全不能用. 假設即時通訊軟體在網路暢通的情況下發送的訊息要求對方10s內就能收到, 如果用輪詢, 那麼客戶端要每隔5s連一次伺服器, 如果在移動端, 手機的電量和流量很快就會被消耗殆盡.
2)SMS(Push)方式:在Android平臺上,你可以通過攔截SMS訊息並且解析訊息內容來了解伺服器的意圖,並獲取其顯示內容進行處理。這個方案的好處是,可以實現完全的實時操作。但是問題是這個方案的成本相對比較高,我們需要向運營商繳納相應的費用。
3)長連線(Push)方式:應用程式和伺服器保持一個長連線,伺服器的訊息可以直接通過這個連結push到應用程式。這個方案可以解決由輪詢帶來的效能問題,但是還是會消耗手機的電池。
Android作業系統允許在低記憶體情況下殺死系統服務,所以我們的推送通知服務很有可能就被作業系統Kill掉了。 輪詢(Pull)方式和SMS(Push)方式這兩個方案也存在明顯的不足。至於持久連線(Push)方案也有不足,不過我們可以通過良好的設計來彌補,以便於讓該方案可以有效的工作。畢竟,我們要知道GMail,GTalk以及GoogleVoice都可以實現實時更新的。
在理論上使用SMS(Push)方式是最好的方法,這種方法手機客戶端最省電(詳情檢視這裡),但是礙於運營商高昂的費用,所以才去長連線(Push)的方式
三、長連線(Push)的三種方法:
3.1、GCM雲端推送功能。
在Android手機平臺上,Google提供了GCM服務,2016 I/O大會發布了Firebase Cloud Messaging (FCM) 用來取代GCM。
Google Cloud Messaging (GCM)是一個用來幫助開發者從伺服器向Android應用程式傳送資料的服務。GCM維護了一個與裝置之間的長連線,該服務提供了一個簡單的、輕量級的機制,允許伺服器可以通知移動應用程式直接與伺服器進行通訊,以便於從伺服器獲取應用程式更新和使用者資料。GCM服務負責處理諸如訊息排隊等事務並向運行於目標裝置上的應用程式分發這些訊息。
圖片來自:http://blog.nkdroidsolutions.com/android-push-notification-with-image/
GCM的使用流程:
Step 1: Android device send sender id to GCM server for registering device
Step 2: GCM Server generate registration id for android device
Step 3: Now our device send registration id to server through API
Step 4: Server save registration id in database for future use
Step 5: For requesting push, our server send request with registration id to GCM server
Step 6: GCM server send push notification to android device based on registration id.
GCM特點
a)Android2.2以下的手機不支援GCM,2.2到3.0需要安裝Google Store並設定Google帳號,4.04及以上版本不需要設定帳號也能支援。
b)GCM只傳遞資料(可以傳遞小於4kb的資料),對這些資料的處理可以全部由開發者控制。
c)Android應用不需要執行就可以接收訊息(通過Android廣播)。
d)GCM不保證傳送的訊息的順序,也不保證訊息一定能夠推送到手機。
GCM心跳策略以及存在的問題
a)用心跳保活長連線,心跳間隔為WIFI下15分鐘,資料網路下28分鐘。
b)Google可以改變所有Android裝置的心跳間隔值(目前還未改變過)。
c)GCM由於心跳間隔固定,並且較長,所以在NAT aging-time設定較小的網路(如聯通2G,或有些WIFI環境下)會導致TCP長連線在下一次心跳前被閘道器釋放。造成Push延遲接收。
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連線也只斷過一次。
在中國電信3G下抓包,大部分時間GCM連線都比較穩定,只會因為偶爾的DHCP造成斷連現象,由於頻率很低(平均數小時才發生一次),對Push體驗的影響不大。
GCM Server型別
GCM提供兩種Server模型:
a)HTTP Server : 使用同步介面傳送HTTP請求,一次請求可以發給最多1000個裝置。
b)XMPP Server :使用非同步介面傳送請求,只支援對單個裝置(或同一個使用者的多個關聯裝置傳送),傳送請求併發數須小於1000,支援裝置到雲端Server傳送資料。需要Google將我們的傳送Server加入白名單。
3.2、自己建立長連線
見下面 第四點
3.3、使用第三方推送平臺
第三方平臺有商用的也有免費的,我們可以根據實現情況使用。
推送平臺:極光、信鴿、一推 等
3.4 XMPP, MQTT等不算推送技術
XMPP是網路即時通訊協議, MQTT是IBM開發的一個即時通訊協議。工作在應用層,並不負責底層的連線是如何保持的。並不關心是使用TCP連線的還是UDP連線的。
也可以使用HTTP協議進行資料傳輸。
如下圖
更多關於XMPP檢視這裡:http://www.cnblogs.com/hanyonglu/archive/2012/03/04/2378956.html
更多關於MQTT檢視這裡:
https://github.com/mcxiaoke/mqtt
四、自己建立長連線
4.1、什麼是長連線
先說短連線, 短連線是通訊雙方有資料互動時就建立一個連線, 資料傳送完成後,則斷開此連線.
長連線就是大家建立連線之後, 不主動斷開. 雙方互相傳送資料, 發完了也不主動斷開連線, 之後有需要傳送的資料就繼續通過這個連線傳送.
TCP連線在預設的情況下就是所謂的長連線, 也就是說連線雙方都不主動關閉連線, 這個連線就應該一直存在。TCP連線其實是一種虛擬連線,連線的狀態資訊是在兩端維持的。
4.2 影響TCP連線壽命的因素
在Android下,不管是GCM,還是微信,都是通過TCP長連線來進行Push訊息的,TCP長連線存活,訊息Push就及時,所以要對影響TCP連線壽命的因素進行研究。
4.2.1 NAT超時
大部分移動無線網路運營商都在鏈路一段時間沒有資料通訊時,會淘汰 NAT 表中的對應項,造成鏈路中斷(NAT超時的更多描述見附錄A)。NAT超時是影響TCP連線壽命的一個重要因素(尤其是國內),所以客戶端自動測算NAT超時時間,來動態調整心跳間隔,是一個重要的優化點。
拓展閱讀:
微信收費事件背後被廣泛忽略的技術細節
微信的大規模使用真的會過多佔用信令,影響通訊穩定嗎?
4.2.2、DHCP的租期
目前測試發現安卓系統對DHCP的處理有Bug,DHCP租期到了不會主動續約並且會繼續使用過期IP,這個問題會造成TCP長連線偶然的斷連。
關於DHCP租期的問題,原文的釋出時Android 6.0還沒有釋出,不知道Android 6.0 還有沒有這個bug(待測試)
4.2.3、網路狀態變化
手機網路和WIFI網路切換、網路斷開和連上等情況有網路狀態的變化,也會使長連線變為無效連線,需要監聽響應的網路狀態變化事件,重新建立Push長連線。
4.3、心跳包的作用
https://www.zhihu.com/question/35013918
我認為心跳包的真正作用有兩個:
4.3.1. 檢測客戶端和服務端的連結是否可用
你ssh連上伺服器的情況下,拔掉網線,你任然檢測不到網路斷開了(沒有FIN),這時候把網線插回去,敲兩下鍵盤,終端一樣有反應,連結還活著。因為tcp的連結是否有效,依賴連結兩端的狀態確定,你在你機器上拔掉網線,你是知道這件事情的,但是中間網路裝置拔掉網線,或者出現什麼問題,你完全無法得知連結是否還有效,依賴tcp本身的keepalive機制需要半個小時以上才能檢測得出來。
4.3.2. 防止客戶端和服務端之間的NAT表因超時而被清理
明確一點, TCP長連線本質上不需要心跳包來維持, 大家可以試一試, 讓兩臺電腦連上同一個wifi, 然後讓其中一臺做伺服器, 另一臺用一個普通的沒有設定KeepAlive的Socket連上伺服器, 只要兩臺電腦別斷網, 路由器也別斷電, DHCP正常續租, 就這麼放著, 過幾個小時再用其中一臺電腦通過之前建立的TCP連線給另一臺發訊息, 另一臺肯定能收到.
那為什麼要有心跳包呢? 其實主要是為了防止上面提到的NAT超時, 既然一些NAT裝置判斷是否淘汰NAT對映的依據是一定時間沒有資料, 那麼客戶端就主動發一個數據,用來延續NAT對映的時長。
4.4、心跳範圍選擇
1、前後臺區分處理:
為了保證微信收訊息及時性的體驗,當微信處於前臺活躍狀態時,使用固定心跳。
微信進入後臺(或者前臺關屏)時,先用幾次最小心跳維持長連結。然後進入後臺自適應心跳計算。這樣做的目的是儘量選擇使用者不活躍的時間段,來減少心跳計算可能產生的訊息不及時收取影響。
2、後臺自適應心跳選擇區間:
可根據自身產品的特點選擇合適的心跳範圍。
4.5 智慧心跳演算法描述
按網路型別區分計算:
因為每個網路的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顯示了在沒有達到穩定網路環境時,我們會一直使用固定短心跳直到滿足三次連續短心跳成功。
原文中缺少了圖4-2和圖4-3,不過通過描述也能理解
使用延遲心跳測試的好處是,可以剔除偶然失敗,和網路變化較大的情況(如地鐵),使測試結果相對可靠(五次延遲測試確定結論)。同時在網路波動較大的情況,使用短心跳,保證收取訊息相對及時。
c) 執行時的動態調整策略(已經按測算心跳穩定值後)
NAT超時值算出來後,在維持心跳的過程中的策略
- 無網路、網路時好時壞、偶然失敗、NAT超時變小:在後臺穩定期發生心跳發生失敗後,我們使用延遲心跳測試法測試五次。如果有一次成功,則保持當前心跳值不變;如果五次測試全失敗,重新計算合理心跳值。該過程如圖4-4所示,有一點需要注意,每個新建的長連線需要先用短心跳成功維持3次後才用successHeart進行心跳。
圖4-4
- NAT超時變大:以周為週期,每週三將後臺穩定態調至自適應計算態,使用心跳延遲法往後探測心跳間隔。
- successHeart是NAT超時臨界值:因為我們現在選擇的是一個比successHeart稍小的值作為穩定值,所以在計算過程中可以避開臨界值。當運營商在我們後臺穩定期將NAT超時調整為我們當前計算值,那麼由於我們每週會去向下探索,所以下一週探測時也可以及時調整正確。
4.6 冗餘Sync和心跳
在使用者的一些主動操作以及聯網狀態改變時,增加冗餘Sync和心跳,確保及時收到訊息。
1、當用戶點亮螢幕的時候,做一次心跳。
2、當微信切換到前臺時,做一次Sync。
3、聯網時重建信令TCP,做一次Sync
4.7、微信使用的推送方案:
微信Push的優化主要有幾個優化點:
a) 公共Push通道
b) 使用GCM Push作為輔助通道
c) 自適應心跳間隔優化
公共Push通道:
由於GCM在國內的可靠性很低,現在國內Android上的Push基本上是各自為政,很多軟體都自己實現Push。導致手機被經常性的喚醒,耗電耗流量嚴重。
市面上已經有很多第三方的公共推送服務,大家可以選擇一個適合自己應用的推送服務。騰訊也有信鴿和維納斯元件,大家在選擇方案的時候可以對比下。
最終因為我們國內外使用一套方案,並且是輔助公道,所以我們選擇使用GCM。
使用GCM Push作為輔助通道:
當前使用GCM的成本不大,可以使用GCM作為輔助通道來增加新訊息的及時性。
使用GCM作為輔助通道,在支援GCM的裝置上微信上傳自己的註冊GCM ID給微信Server。
微信Server在發現長連線失效的情況下,可以使用GCM 作為輔助通道通知客戶端有新訊息,客戶端收到push通知後做一次sync。
只利用GCM來啟用微信,不傳遞訊息的具體資料,要控制給同一裝置傳送GCM通知的時間間隔(如五分鐘)。
5、Android休眠帶來的影響
Android裝置上解決耗電的一個策略就是休眠,手機在鎖屏之後一段時間手機就會休眠,那個時候,無論是螢幕,CPU還是其他模組都會停止工作,這樣導致了2個問題:
1.一些通訊軟體的心跳包中斷,導致掉線
2.若採用UDP連線的情況下,伺服器過來的資料包不一定實時。
我們來講講如何解決以上的兩個問題。
Android手機有兩個處理器
- Application Processor(AP)
- Baseband Processor(BP)。
AP是ARM架構的處理處理器,用於執行Linux+Android體系;
BP用於執行及時操縱體系(RTOS),通訊協議棧運行於BP的RTOS之上。非通話時候,BP的能耗基本在5mA以下,而AP只要處於非休眠狀況,能耗至少在50mA以上,履行圖形運算時會更高。別的LCD工作時功耗在100mA左右,WIFI也在100mA左右。一般手機待機時,AP、LCD、WIFI均進入休眠狀況,這時Android中應用法度的程式碼也會停止執行。
Android為了確保一些關鍵程式碼的正確執行,供給了Wake Lock的API,使得應用有許可權經由過程程式碼阻攔AP進入休眠狀況(iOS、WP7都沒這種器材)。若是不懂得Android設計者的意圖而濫用Wake Lock API,為了自身程式碼在後臺的正常工作而長時候阻攔AP進入休眠狀況,結果就相當嚴重了,手機的電量就犀利嘩啦的被用完了。
如果AP休眠了,手機不是接收不到訊息了嗎?
完全不用擔心,通訊協議棧運行於BP,一旦收到資料包,BP會將AP喚醒,喚醒的時間足夠AP完成對BP收到協議的處理,但是有一點需要大家注意的是,假如你處理協議包的時間很長的話,那麼請加上wakelock,完成之後再釋放掉。
如果AP休眠了,程式如何執行向伺服器傳送心跳包的邏輯
你顯然不能靠AP來做心跳計時. Android提供的Alarm Manager就是來解決這個問題的. Alarm應該是BP計時(或其它某個帶石英鐘的晶片,不太確定,但絕對不是AP), 觸發時喚醒AP執行程式程式碼.
那麼Wake Lock API有啥用呢? 比如心跳包從請求到應答, 比如斷線重連重新登陸這些關鍵邏輯的執行過程, 就需要Wake Lock來保護. 而一旦一個關鍵邏輯執行成功, 就應該立即釋放掉Wake Lock了. 兩次心跳請求間隔5到10分鐘, 基本不會怎麼耗電. 除非網路不穩定. 頻繁斷線重連, 那種情況辦法不多.
上面所說的通訊協議, 我猜應該是無線資源控制協議(Radio Resource Control), RRC應該工作在OSI參考模型中的第三層網路層, 而TCP, UDP工作在第四層傳輸層, 上文說的BP, 應該就是手機中的基帶, 也有叫Radio的,
Google在Optimizing Downloads for Efficient Network Access 中提到了一個叫Radio State Machine的東西
TCP長連線是可以將AP喚醒,但是UDP資料包並不會喚醒。
具體的原因可能是因為底層對於TCP長連線的資料過來,會產生AP中斷來喚醒AP,但是UDP不會。。。這麼做也是有道理的,因為TCP長連結是客戶端自身驗證過的伺服器,也就是資料來源可靠。。。若UDP也會喚醒,那完全可以進行UDP資料包工具,這樣一來,被攻擊的手機至少耗電量將會大幅度上升。
附錄A——NAT超時介紹
因為 IP v4 的 IP 量有限,運營商分配給手機終端的 IP 是運營商內網的 IP,手機要連線 Internet,就需要通過運營商的閘道器做一個網路地址轉換(Network Address Translation,NAT)。簡單的說運營商的閘道器需要維護一個外網 IP、埠到內網 IP、埠的對應關係,以確保內網的手機可以跟 Internet 的伺服器通訊。
大部分移動無線網路運營商都在鏈路一段時間沒有資料通訊時,會淘汰 NAT 表中的對應項,造成鏈路中斷。下表列出一些已測試過的網路的NAT超時時間(更多資料由於測試條件所限沒有測到):
地區/網路 | NAT超時時間 |
---|---|
中國移動3G和2G | 5分鐘 |
中國聯通2G | 5分鐘 |
中國電信3G | 大於28分鐘 |
美國3G | 大於28分鐘 |
臺灣3G | 大於28分鐘 |
GGSN(Gateway GPRS Support Node 閘道器GPRS支援結點)模組就實現了NAT功能。
因為大部分移動無線網路運營商都是為了減少閘道器的NAT對映表的負荷,所以如果發現鏈路中有一段時間沒有資料通訊時,會刪除其對應表,造成鏈路中斷。
長連線心跳間隔必須要小於NAT超時時間(aging-time),如果超過aging-time不做心跳,TCP長連線鏈路就會中斷,Server就無法傳送Push給手機,只能等到客戶端下次心跳失敗後,重建連線才能取到訊息。
附錄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。
參考:
Android推送技術研究
Android實現推送方式解決方案
Android微信智慧心跳方案
Android休眠問題探討
關注我的公眾號,輕鬆瞭解和學習更多技術
再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智慧的隊伍中來!https://blog.csdn.net/jiangjunshow