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程序犭袁。
参考连接: