http://www.cocoachina.com/ios/20160426/16013.htmlhtml
本文爲投稿文章,做者:iOS程序犭袁 (博客)ios
前言:面試
APNs 協議在近兩年的 WWDC 上改過兩次,2015年12月17日更是推出了革命性的新特性。但在國內傳播的博客、面試題裏關於APNs的答案全都是舊的、錯的。bootstrap
正文:服務器
對 APNs 的吐槽網絡
APNs 是 Apple Push Notification service 的簡稱(注意 APNs 的大小寫, s不須要大寫)。session
如下是我收集的一些關於 APNs 的吐槽,你先看下哪些吐槽比較「到位」:架構
答案會穿插在下文中。app
APNs新聞異步
2014年6月份WWDC搭載iOS8及以上系統的iOS設備,可以接收的最大playload大小提高到2KB。低於iOS8的設備以及OS X設備維持256字節。參考文檔:What's New in Notifications - WWDC 2014 - Session 713 - iOS
2015年6月份WWDC宣佈將在不久的未來發布 「基於 HTTP/2 的全新 APNs 協議」,並在大會上發佈了僅僅支持測試證書的版本。參考文檔:What's New in Notifications - WWDC 2015 - Session 720 - iOS, OS X
2015年12月17日起,發佈 「基於 HTTP/2 的全新 APNs 協議」,iOS 系統以及 OS X 系統,統一將最大 playload 大小提高到4KB。參考文檔:Apple Push Notification Service Update 12-17 2015
新舊 APNs 協議工做示意圖對比
接下來咱們分別對新舊協議進行一下介紹:
反人類的舊APNs協議設計
在介紹新版 APNs 前,讓咱們來吐槽下舊的基於二進制的 APNs 協議設計是多麼反人類:
在理論上,推送分發的服務器要打開一個同 APNs 網關服務器的
鏈接,並保持這個鏈接。但在舊的協議下,APNs 服務卻不保證 socket 能維持這個鏈接。若是通道上沒有消息往來,空閒下來到話,socket將被路由掐斷。也就是說:APNs 鏈接說斷就斷,而你無能爲力。有意思的是:在舊的協議下,若是服務器響應成功的話,你將不會收到任何迴應,可是若是服務器響應失敗(例如,使用了一個非法的 Push token),服務器將返回了一個錯誤編碼,並關閉這個socket。最重要的是,你必須從新發送使用這個無效 token 之後發送的全部推送(詳情見示意圖)。所以,你可能一直不能肯定你的推送是否成功的被 APNs 服務器接收。
成功了不響應,失敗了才響應,這個是最大的反人類。因而許多開發者想到了一個很 tricky 的辦法:利用這個「漏洞」,好比在每發送10條後故意發送一個錯誤的token,若是APNs有響應了,就能夠確認 APNs 是處在可用狀態的,進而確認這10條消息是發送成功的。若是沒有響應就說明可能鏈接已經中斷,那麼這10條消息極可能是丟失的,而後作進一步的處理。但代價顯而易見:將致使大家的推送系統性能低下。(本文中所說到「大家的推送系統」,若是是使用的第三方的SDK完成的推送服務,那麼就是指SDK提供商所搭建的推送系統。若是是大家公司本身搭建的推送系統,那麼就是指大家本身的推送系統。)蘋果有一個名爲"feedback"的服務,咱們能夠定時調用這個服務來獲取invalid tokens的列表。這個服務你只要調用一次就能夠得到全部的invalid tokens 列表。因此,若是一個應用使用了不少不一樣公司的推送SDK,他們將會爭奪資源去輪詢查找invalid tokens列表。invalid token越多,大家的推送系統性能將越低。並且 APNs 只要一發生錯誤就關閉這個鏈接,而後從新鏈接。也就是「重啓」 socket 鏈接。
示意圖:
圖中的 PN2 去哪裏了?它被放到了 feedback 列表裏,等待下次你調用 feedback 服務,而後重發。
爲何Apple要在舊APNs中設計出「重啓」的策略?爲了效率。就像PC機出問題,咱們總說「重啓能解決90%的問題」。
爲了理解「重啓」策略,咱們能夠類比下,熟悉 Erlang/OTP 的朋友可能知道, Erlang/OTP 在處理錯誤方面有獨到之處:監督樹(supervision trees)。大體來講,每個 Erlang 進程都由一個監督進程發起並監視。當一個進程遇到了問題的時候,它就會退出。當進程退出的時候,其監督進程會將其重啓。
(這些監督進程由一個引導進程(bootstrap process)發起,當監督進程遇到錯誤的時候,引導進程會將其重啓)
其思想是,快速的失敗而後重啓比去處理錯誤要快。像這樣的錯誤處理看起來跟直覺相反 —— 當錯誤發生的時候經過放棄處理來得到可靠性。可是重啓的確是解決暫時性錯誤的靈丹妙藥。
這也可能讓你想到 DNS 服務發展史:
DNS 在設計之初是基於 UDP 的,顯然這樣的設計不能知足當今社會的準確性的需求,因而涌現瞭如 DNSPod 這樣的基於 HTTP 的 DNS 解析服務。可是當時爲何這樣設計,實際也很好理解,UDP 效率高,一來一回網絡上傳輸的只有兩個包,而 HTTP則須要三次握手三個包,再一拆包,就須要四個包。這是受限於當時整個社會的帶寬水平較低,而如今沒人會感激 UDP 所節省的流量,全部人都在詬病DNS污染問題。這樣你也許就理解了,爲何舊的 APNs 設計如此反人類。這個是必經階段。
那麼接下來就讓咱們看看Apple爲解決這些問題而推出的基於 HTTP/2 的全新 APNs 協議。
基於 HTTP/2 的全新 APNs 協議
來看下新版的 APNs 的新特性:
1)Request 和 Response 支持JSON網絡協議
2)APNs支持狀態碼和返回 error 信息
APNs推送成功時 Response 將返回狀態碼200,遠程通知是否發送成功不再用靠猜了!
APNs推送失敗時,Response 將返回 JSON 格式的 Error 信息。
3)最大推送長度提高到4096字節(4Kb)
4)能夠經過 「HTTP/2 PING 」 心跳包功能檢測當前 APNs 鏈接是否可用,並能維持當前長鏈接。
5)支持爲不一樣的推送類型定義 「topic」 主題
6)不一樣推送類型,只須要一種推送證書 Universal Push Notification Client SSL 證書。
示意圖:
其中最大的變化就是基於了 HTTP/2 協議,採用了長鏈接設計,並提供 「HTTP/2 PING 」 心跳包功能檢測、維持當前 APNs 鏈接,解決了老 APNs 沒法維持鏈接的問題。
並且新增的狀態碼特性,也解決了這個問題:沒法獲知消息是否成功地從大家的推送系統投遞到了 APNs 上。理論上,大家能夠保證消息是100%投遞到了APNs的,由於你能夠準確的知道哪條消息到達了APNs,哪些沒到。重發特定失敗消息成爲可能。
因此上文開頭的吐槽中第一條,有一句是「不到位的」,由於如今SDK的提供商可以準確地告訴你哪些消息推送到APNs了,哪些沒有。
順便介紹下 HTTP/2:
HTTP/2 是 HTTP 協議發佈後的首個更新,於2015年2月17日被批准。它採用了一系列優化技術來總體提高 HTTP 協議的傳輸性能,如異步鏈接複用、頭壓縮等等,可謂是當前互聯網應用開發中,網絡層次架構優化的首選方案之一。
Apple 對於 HTTP/2 的態度也很是積極,2015年5月 HTTP/2 正式發表後不久,便在緊接着6月召開的WWDC 2015大會中,向全球開發者宣佈,iOS 9 開始支持HTTP/2。
並且若是咱們要使用 HTTP/2,那麼在網絡庫的選擇上必然要使用 NSURLSession。
咱們都知道 HTTP/2 是複用 TCP 管道鏈接的,並且 HTTP/2 也以高複用著稱,這也使新的 APNs 協議更加高性能。(題外話:這點也一樣體如今 NSURLSession 底層對於每一個 session 是對多個 task 進行鏈接的複用。)
Universal Push Notification Client SSL 證書
在開發中,每每一條內容,須要向多個終端進行推送,終端有:iOS、tvOS、 and OS X devices, 和藉助iOS來實現推送的 Apple Watch。在以往的開發中,不一樣的推送,須要配置不一樣的推送證書:咱們須要配置:dev證書、prod證書、VOIP證書、等等。而從2015年12月17日起,只使用一種證書就能夠了,再也不須要那麼多證書,這種證書就叫作Universal Push Notification Client SSL 證書(下文統一簡稱:Universal推送證書)。
改進了,但仍需改進。仍是有坑
APNs的確改進來很多,但仍有須要改進對地方。仍是有坑:
除了獲取TLS證書比較複雜未解決外,還有一些坑:
文章開頭提到過如下這種狀況:
很遺憾的告訴你,你的吐槽是「到位的」:你之後還得忍受這種反人類的設計。
這中間發生了什麼?
當 APNs 向你發送了4條推送,可是你的設備網絡情況很差,在 APNs 那裏下線了,這時 APNs 到你的手機的鏈路上有4條任務堆積,APNs 的處理方式是,只保留最後一條消息推送給你,而後告知你推送數。那麼其餘三條消息呢?會被APNs丟棄。
有一些 App 的 IM 功能沒有維持長鏈接,是徹底經過推送來實現到,一般狀況下,這些 App 也已經考慮到了這種丟推送的狀況,這些 App 的作法都是,每次收到推送以後,而後向本身的服務器查詢當前用戶的未讀消息。可是APNs也一樣沒法保證這四條推送能至少有一條到達你的 App。很遺憾的告訴這些App,此次的更新對大家所遭受對這些坑,沒有改善。
爲何這麼設計?APNs的存儲-轉發能力太弱,大量的消息存儲和轉發將消耗Apple服務器的資源,多是出於存儲成本考慮,也多是由於 Apple 轉發能力太弱。總之結果就是 APNs 歷來不保證消息的達到率。而且設備上線以後也不會向服務器上傳信息。
因此上文開頭的吐槽中第一條,也有一句是「到位的」,由於如今SDK的提供商依然沒法保證,消息推到了 APNs,APNs能推到 App 那裏。
但Google Cloud Messaging就有這些特性。並且 GCM 如今也支持iOS設備了,那麼 APNs 和 GCM 如今就造成了競爭關係。讓我共同期待 APNs 在2016年6月的 WWDC 的能有新的改進吧。
對App開發的影響
想使用新協議,若是你用的第三方推送,這裏最明顯的操做,就是你必須更新到支持新協議的SDK版本。由於新協議須要 SDK 上傳你 app 的 bundle id ,生成各個平臺推送用的 topic。若是大家本身搭建的服務,則須要你本身上傳。老協議不用上傳。
新 APNs 支持 iOS6 等全版本推送內容達4096字節,舊 APNs 是14年6月以前只支持256字節,在此以後支持 iOS8 以上2048字節。之前受限於推送字節,好比推文章 url,開發者選擇超出256後推送id,甚至不判斷直接推 id,接收後再請求完整 url。一旦請求錯誤,推送內容可能丟失。如今能夠避免了。
如何建立 Universal Push Notification Client SSL 證書
如今你知道什麼是 Universal Push Notification Client SSL 證書了,那麼如何建立它?
圖中其餘方式,就叫作非 Universal 方式(下文簡稱:非 Universal 推送證書):
這裏也推薦使用 Universal 推送證書來進行推送服務。詳細的建立步驟以下所示:
前往蘋果開發者中心進行登陸,並點擊 「Certificates, Identifiers & Profiles」。
選擇在 Certificates 欄下的「All」。
點擊下圖中紅色邊框內的加號按鈕。
選擇 「Production」 欄下的 「Apple Push Notification service SSL (Sandbox & Production)」 勾選後,點擊下一步。
從 App ID 下拉菜單中選擇你須要的 App ID ,點擊下一步。
這時會出現 About Creating a Certificate Signing Request (CSR)。
根據它的說明建立 Certificate Signing Request。
點擊下圖中的 「Choose File」 按鈕:
上傳剛剛生成的 .certSigningRequest 文件 生成 APNs Push Certificate。
下載證書。
雙擊打開證書,證書打開時會啓動鑰匙串訪問工具。
在鑰匙串訪問工具中,你的證書會顯示在 「證書」 中,注意選擇左下角的 「證書」 和左上角 「登陸」。
結束語
對於 APNs 而言,iOS 9 的這一更新是有劃時代意義的,請即刻敦促大家公司的服務端進行升級,或者使用支持新 APNs 協議的 SDK 進行推送服務。 文中若有錯誤,並請幫忙指正,反饋請發往微博@iOS程序犭袁。
參考連接: