前言: APNs 協(xié)議在近兩年的 WWDC 上改過兩次叉橱, 15 年 12 月 17 日更是推出了革命性的新特性。但在國內(nèi)傳播的博客者蠕、面試題里關(guān)于 APNs 的答案全都是舊的窃祝、錯(cuò)的。
導(dǎo)航:
- 對 APNs 的吐槽
- APNs新聞一欄
- 新舊 APNs 協(xié)議工作示意圖對比
- 反人類的舊APNs協(xié)議設(shè)計(jì)
- 基于 HTTP/2 的全新 APNs 協(xié)議
- 改進(jìn)了踱侣,但仍需改進(jìn)粪小。還是有坑
- 對App開發(fā)的影響
- 如何創(chuàng)建 Universal Push Notification Client SSL 證書
- 結(jié)束語
對 APNs 的吐槽
APNs 是 Apple Push Notification service 的簡稱(注意 APNs 的大小寫, s不需要大寫)大磺。
以下是我收集的一些關(guān)于 APNs 的吐槽,你先看下哪些吐槽比較“到位”:
-- | 被吐槽的內(nèi)容 | 吐槽 |
---|---|---|
1 | 使用第三方SDK接入推送服務(wù)探膊,SDK提供商卻告訴我杠愧,他們無法獲知哪條消息成功發(fā)送給了APNs,哪些失敗了逞壁,而且即使APNs接收了流济,APNs是否能保證投遞成功,他們也無能為力腌闯。 | 我把消息交給你了绳瘟,你告訴什么都保證不了?推送成功與否”基本靠猜“姿骏? |
2 |
|
為什么我推了多條消息糖声,APNs就只給我最后一條?分瘦! |
3 | 推送內(nèi)容只能是 256 字節(jié) | 這也太小了蘸泻,根本不夠用啊嘲玫! |
4 | 生產(chǎn)環(huán)境推送證書悦施、測試環(huán)境推送證書、tvOS推送證書趁冈、watchOS推送證書歼争、VOIP推送證書。渗勘。 | 證書太多了沐绒,制作、切換證書太麻煩旺坠! |
答案會穿插在下文中乔遮。
APNs新聞一欄
時(shí)間 | 新聞 | 參考文檔 |
---|---|---|
2014年6月 | 2014年6月份WWDC搭載iOS8及以上系統(tǒng)的iOS設(shè)備,能夠接收的最大playload大小提升到2KB取刃。低于iOS8的設(shè)備以及OS X設(shè)備維持256字節(jié)蹋肮。 |
What's New in Notifications - WWDC 2014 - Session 713 - iOS |
2015年6月 | 2015年6月份WWDC宣布將在不久的將來發(fā)布 “基于 HTTP/2 的全新 APNs 協(xié)議”,并在大會上發(fā)布了僅僅支持測試證書的版本璧疗。 |
What's New in Notifications - WWDC 2015 - Session 720 - iOS, OS X |
2015年12月17日 | 2015年12月17日起坯辩,發(fā)布 “基于 HTTP/2 的全新 APNs 協(xié)議”,iOS 系統(tǒng)以及 OS X 系統(tǒng),統(tǒng)一將最大 playload 大小提升到4KB崩侠。 | Apple Push Notification Service Update 12-17 2015 |
新舊 APNs 協(xié)議工作示意圖對比
基于 HTTP/2 的新 APNs 協(xié)議 | 基于二進(jìn)制的舊 APNs 協(xié)議 |
---|---|
接下來我們分別對新舊協(xié)議進(jìn)行一下介紹:
反人類的舊APNs協(xié)議設(shè)計(jì)
在介紹新版 APNs 前漆魔,讓我們來吐槽下舊的基于二進(jìn)制的 APNs 協(xié)議設(shè)計(jì)是多么反人類:
在理論上,推送分發(fā)的服務(wù)器要打開一個(gè)同 APNs 網(wǎng)關(guān)服務(wù)器的
連接,并保持這個(gè)連接改抡。但在舊的協(xié)議下矢炼,APNs 服務(wù)卻不保證 socket 能維持這個(gè)連接。如果通道上沒有消息往來阿纤,空閑下來到話句灌,socket將被路由掐斷。也就是說:APNs 連接說斷就斷欠拾,而你無能為力胰锌。有意思的是:在舊的協(xié)議下,如果服務(wù)器響應(yīng)成功的話清蚀,你將不會收到任何回應(yīng)匕荸,但是如果服務(wù)器響應(yīng)失敗(例如枷邪,使用了一個(gè)非法的 Push token),服務(wù)器將返回了一個(gè)錯(cuò)誤編碼诺凡,并關(guān)閉這個(gè)socket东揣。最重要的是,你必須重新發(fā)送使用這個(gè)無效 token 以后發(fā)送的所有推送(詳情見示意圖)腹泌。因此嘶卧,你可能一直不能確定你的推送是否成功的被 APNs 服務(wù)器接收。
成功了不響應(yīng)凉袱,失敗了才響應(yīng)芥吟,這個(gè)是最大的反人類。于是許多開發(fā)者想到了一個(gè)很 tricky 的辦法:利用這個(gè)“漏洞”专甩,比如在每發(fā)送10條后故意發(fā)送一個(gè)錯(cuò)誤的token钟鸵,如果APNs有響應(yīng)了,就可以確認(rèn) APNs 是處在可用狀態(tài)的涤躲,進(jìn)而確認(rèn)這10條消息是發(fā)送成功的棺耍。如果沒有響應(yīng)就說明可能連接已經(jīng)中斷,那么這10條消息很可能是丟失的种樱,然后做進(jìn)一步的處理蒙袍。但代價(jià)顯而易見:將導(dǎo)致你們的推送系統(tǒng)性能低下。(本文中所說到“你們的推送系統(tǒng)”嫩挤,如果是使用的第三方的SDK完成的推送服務(wù)害幅,那么就是指SDK提供商所搭建的推送系統(tǒng)。如果是你們公司自己搭建的推送系統(tǒng)岂昭,那么就是指你們自己的推送系統(tǒng)以现。)蘋果有一個(gè)名為"feedback"的服務(wù),我們可以定時(shí)調(diào)用這個(gè)服務(wù)來獲取invalid tokens的列表。這個(gè)服務(wù)你只要調(diào)用一次就可以獲得所有的invalid tokens 列表叼风。所以取董,如果一個(gè)應(yīng)用使用了很多不同公司的推送SDK,他們將會爭奪資源去輪詢查找invalid tokens列表无宿。invalid token越多茵汰,你們的推送系統(tǒng)性能將越低。而且 APNs 只要一發(fā)生錯(cuò)誤就關(guān)閉這個(gè)連接孽鸡,然后重新連接蹂午。也就是“重啟” socket 連接。
示意圖:
圖中的 PN2 去哪里了彬碱?它被放到了 feedback 列表里豆胸,等待下次你調(diào)用 feedback 服務(wù),然后重發(fā)巷疼。
為什么Apple要在舊APNs中設(shè)計(jì)出“重啟”的策略晚胡?
為了效率。
就像PC機(jī)出問題嚼沿,我們總說“重啟能解決90%的問題”估盘。
為了理解“重啟”策略,我們可以類比下骡尽,熟悉 Erlang/OTP 的朋友可能知道遣妥, Erlang/OTP 在處理錯(cuò)誤方面有獨(dú)到之處:監(jiān)督樹(supervision trees)。大致來說攀细,每一個(gè) Erlang 進(jìn)程都由一個(gè)監(jiān)督進(jìn)程發(fā)起并監(jiān)視箫踩。當(dāng)一個(gè)進(jìn)程遇到了問題的時(shí)候,它就會退出谭贪。當(dāng)進(jìn)程退出的時(shí)候境钟,其監(jiān)督進(jìn)程會將其重啟。
(這些監(jiān)督進(jìn)程由一個(gè)引導(dǎo)進(jìn)程(bootstrap process)發(fā)起故河,當(dāng)監(jiān)督進(jìn)程遇到錯(cuò)誤的時(shí)候吱韭,引導(dǎo)進(jìn)程會將其重啟)
其思想是,快速的失敗然后重啟比去處理錯(cuò)誤要快鱼的。像這樣的錯(cuò)誤處理看起來跟直覺相反 —— 當(dāng)錯(cuò)誤發(fā)生的時(shí)候通過放棄處理來獲得可靠性理盆。但是重啟的確是解決暫時(shí)性錯(cuò)誤的靈丹妙藥。
這也可能讓你想到 DNS 服務(wù)發(fā)展史:
DNS 在設(shè)計(jì)之初是基于 UDP 的凑阶,顯然這樣的設(shè)計(jì)不能滿足當(dāng)今社會的準(zhǔn)確性的需求猿规,于是涌現(xiàn)了如 DNSPod 這樣的基于 HTTP 的 DNS 解析服務(wù)。但是當(dāng)時(shí)為什么這樣設(shè)計(jì)宙橱,實(shí)際也很好理解姨俩,UDP 效率高蘸拔,一來一回網(wǎng)絡(luò)上傳輸?shù)闹挥袃蓚€(gè)包,而 HTTP則需要三次握手三個(gè)包环葵,再一拆包调窍,就需要四個(gè)包。這是受限于當(dāng)時(shí)整個(gè)社會的帶寬水平較低张遭,而現(xiàn)在沒人會感激 UDP 所節(jié)省的流量邓萨,所有人都在詬病DNS污染問題。這樣你也許就理解了菊卷,為什么舊的 APNs 設(shè)計(jì)如此反人類缔恳。這個(gè)是必經(jīng)階段。
那么接下來就讓我們看看Apple為解決這些問題而推出的基于 HTTP/2 的全新 APNs 協(xié)議洁闰。
基于 HTTP/2 的全新 APNs 協(xié)議
來看下新版的 APNs 的新特性:
- Request 和 Response 支持JSON網(wǎng)絡(luò)協(xié)議
- APNs支持狀態(tài)碼和返回 error 信息
- APNs推送成功時(shí) Response 將返回狀態(tài)碼200歉甚,遠(yuǎn)程通知是否發(fā)送成功再也不用靠猜了!
- APNs推送失敗時(shí)扑眉,Response 將返回 JSON 格式的 Error 信息纸泄。
- 最大推送長度提升到4096字節(jié)(4Kb)
- 可以通過 “HTTP/2 PING ” 心跳包功能檢測當(dāng)前 APNs 連接是否可用,并能維持當(dāng)前長連接襟雷。
- 支持為不同的推送類型定義 “topic” 主題
- 不同推送類型刃滓,只需要一種推送證書 Universal Push Notification Client SSL 證書。
示意圖:
其中最大的變化就是基于了 HTTP/2 協(xié)議耸弄,采用了長連接設(shè)計(jì),并提供 “HTTP/2 PING ” 心跳包功能檢測卓缰、維持當(dāng)前 APNs 連接计呈,解決了老 APNs 無法維持連接的問題。
而且新增的狀態(tài)碼特性征唬,也解決了這個(gè)問題:無法獲知消息是否成功地從你們的推送系統(tǒng)投遞到了 APNs 上捌显。理論上,你們可以保證消息是100%投遞到了APNs的总寒,因?yàn)槟憧梢詼?zhǔn)確的知道哪條消息到達(dá)了APNs扶歪,哪些沒到。重發(fā)特定失敗消息成為可能摄闸。
所以上文開頭的吐槽中第一條善镰,有一句是“不到位的”,因?yàn)楝F(xiàn)在SDK的提供商能夠準(zhǔn)確地告訴你哪些消息推送到APNs了年枕,哪些沒有炫欺。
順便介紹下 HTTP/2:
HTTP/2 是 HTTP 協(xié)議發(fā)布后的首個(gè)更新,于2015年2月17日被批準(zhǔn)熏兄。它采用了一系列優(yōu)化技術(shù)來整體提升 HTTP 協(xié)議的傳輸性能品洛,如異步連接復(fù)用树姨、頭壓縮等等,可謂是當(dāng)前互聯(lián)網(wǎng)應(yīng)用開發(fā)中桥状,網(wǎng)絡(luò)層次架構(gòu)優(yōu)化的首選方案之一帽揪。
Apple 對于 HTTP/2 的態(tài)度也非常積極,2015年5月 HTTP/2 正式發(fā)表后不久辅斟,便在緊接著6月召開的WWDC 2015大會中转晰,向全球開發(fā)者宣布,iOS 9 開始支持HTTP/2砾肺。
而且如果我們要使用 HTTP/2挽霉,那么在網(wǎng)絡(luò)庫的選擇上必然要使用 NSURLSession。
我們都知道 HTTP/2 是復(fù)用 TCP 管道連接的变汪,而且 HTTP/2 也以高復(fù)用著稱侠坎,這也使新的 APNs 協(xié)議更加高性能。(題外話:這點(diǎn)也同樣體現(xiàn)在 NSURLSession 底層對于每個(gè) session 是對多個(gè) task 進(jìn)行連接的復(fù)用裙盾。)
Universal Push Notification Client SSL 證書
在開發(fā)中实胸,往往一條內(nèi)容,需要向多個(gè)終端進(jìn)行推送番官,終端有:iOS庐完、tvOS、 and OS X devices, 和借助iOS來實(shí)現(xiàn)推送的 Apple Watch徘熔。在以往的開發(fā)中门躯,不同的推送,需要配置不同的推送證書:我們需要配置:dev證書酷师、prod證書讶凉、VOIP證書、等等山孔。而從2015年12月17日起懂讯,只使用一種證書就可以了,不再需要那么多證書台颠,這種證書就叫做Universal Push Notification Client SSL 證書(下文統(tǒng)一簡稱:Universal推送證書)褐望。
改進(jìn)了,但仍需改進(jìn)串前。還是有坑
APNs的確改進(jìn)來不少瘫里,但仍有需要改進(jìn)對地方。還是有坑:
除了獲取TLS證書比較復(fù)雜未解決外酪呻,還有一些坑:
文章開頭提到過以下這種情況:
很遺憾的告訴你减宣,你的吐槽是“到位的”:你以后還得忍受這種反人類的設(shè)計(jì)。
這中間發(fā)生了什么玩荠?
當(dāng) APNs 向你發(fā)送了多條推送漆腌,但是你的設(shè)備網(wǎng)絡(luò)狀況不好贼邓,在 APNs 那里下線了,這時(shí) APNs 到你的手機(jī)的鏈路上有多條任務(wù)堆積闷尿,APNs 的處理方式是塑径,只保留最后一條消息推送給你,然后告知你推送數(shù)填具。那么其他三條消息呢统舀?會被APNs丟棄。
有一些 App 的 IM 功能沒有維持長連接劳景,是完全通過推送來實(shí)現(xiàn)到誉简,通常情況下,這些 App 也已經(jīng)考慮到了這種丟推送的情況盟广,這些 App 的做法都是闷串,每次收到推送之后,然后向自己的服務(wù)器查詢當(dāng)前用戶的未讀消息筋量。但是APNs也同樣無法保證這多條推送能至少有一條到達(dá)你的 App烹吵。很遺憾的告訴這些App,這次的更新對你們所遭受對這些坑桨武,沒有改善肋拔。
為什么這么設(shè)計(jì)?APNs的存儲-轉(zhuǎn)發(fā)能力太弱呀酸,大量的消息存儲和轉(zhuǎn)發(fā)將消耗Apple服務(wù)器的資源凉蜂,可能是出于存儲成本考慮,也可能是因?yàn)?Apple 轉(zhuǎn)發(fā)能力太弱性誉≡颈梗總之結(jié)果就是 APNs 從來不保證消息的達(dá)到率。并且設(shè)備上線之后也不會向服務(wù)器上傳信息艾栋。
所以上文開頭的吐槽中第一條,也有一句是“到位的”蛉顽,因?yàn)楝F(xiàn)在SDK的提供商依然無法保證蝗砾,消息推到了 APNs,APNs能推到 App 那里携冤。
但Google Cloud Messaging就有這些特性悼粮。而且 GCM 現(xiàn)在也支持iOS設(shè)備了,那么 APNs 和 GCM 現(xiàn)在就形成了競爭關(guān)系曾棕。讓我共同期待 APNs 在2016年6月的 WWDC 的能有新的改進(jìn)吧扣猫。
對App開發(fā)的影響
想使用新協(xié)議,如果你用的第三方推送翘地,這里最明顯的操作申尤,就是你必須更新到支持新協(xié)議的SDK版本癌幕。因?yàn)樾聟f(xié)議需要 SDK 上傳你 app 的 bundle id ,生成各個(gè)平臺推送用的 topic。如果你們自己搭建的服務(wù)昧穿,則需要你自己上傳勺远。老協(xié)議不用上傳。
新 APNs 支持 iOS6 等全版本推送內(nèi)容達(dá)4096字節(jié)时鸵,舊 APNs 是14年6月之前只支持256字節(jié)胶逢,在此之后支持 iOS8 以上2048字節(jié)。以前受限于推送字節(jié)饰潜,比如推文章 url初坠,開發(fā)者選擇超出256后推送id,甚至不判斷直接推 id彭雾,接收后再請求完整 url碟刺。一旦請求錯(cuò)誤,推送內(nèi)容可能丟失」邗危現(xiàn)在可以避免了南誊。
如何創(chuàng)建 Universal Push Notification Client SSL 證書
現(xiàn)在你知道什么是 Universal Push Notification Client SSL 證書了,那么如何創(chuàng)建它蜜托?
圖中其他方式抄囚,就叫做非 Universal 方式(下文簡稱:非 Universal 推送證書):
這里也推薦使用 Universal 推送證書來進(jìn)行推送服務(wù)。詳細(xì)的創(chuàng)建步驟如下所示:
- 前往蘋果開發(fā)者中心進(jìn)行登錄橄务,并點(diǎn)擊 “Certificates, Identifiers & Profiles”幔托。
- 選擇在 Certificates 欄下的“All”。
-
點(diǎn)擊下圖中紅色邊框內(nèi)的加號按鈕蜂挪。
-
選擇 “Production” 欄下的 “Apple Push Notification service SSL (Sandbox & Production)” 勾選后重挑,點(diǎn)擊下一步。
-
從 App ID 下拉菜單中選擇你需要的 App ID 棠涮,點(diǎn)擊下一步谬哀。
- 這時(shí)會出現(xiàn) About Creating a Certificate Signing Request (CSR)。
根據(jù)它的說明創(chuàng)建 Certificate Signing Request严肪。
-
點(diǎn)擊下圖中的 “Choose File” 按鈕:
- 上傳剛剛生成的 .certSigningRequest 文件 生成 APNs Push Certificate史煎。
- 下載證書。
- 雙擊打開證書驳糯,證書打開時(shí)會啟動(dòng)鑰匙串訪問工具篇梭。
在鑰匙串訪問工具中,你的證書會顯示在 “證書” 中酝枢,注意選擇左下角的 “證書” 和左上角 “登錄”恬偷。
結(jié)束語
對于 APNs 而言,iOS9 的這一更新是有劃時(shí)代意義的帘睦,請即刻敦促你們公司的服務(wù)端進(jìn)行升級袍患,或者使用支持新 APNs 協(xié)議的 SDK 進(jìn)行推送服務(wù)坦康。 文中如有錯(cuò)誤,并請幫忙指正协怒,反饋請發(fā)往微博@iOS程序犭袁涝焙。
參考鏈接: