基于HTTP2的全新APNs协议
您目前处于:编程  2017年01月09日

对 APNs 的吐槽

APNs 是 Apple Push Notification service 的简称(注意 APNs 的大小写, s不需要大写)。

以下是一些关于 APNs 的吐槽,你先看下哪些吐槽比较到位:

1. 我把消息交给你了,你告诉什么都保证不了?推送成功与否“基本靠猜“?

使用第三方SDK接入推送服务,SDK提供商却告诉我,他们无法获知哪条消息成功发送给了APNs,哪些失败了,而且即使APNs接收了,APNs是否能保证投递成功,他们也无能为力。

2. 为什么我推了多条消息,APNs就只给我最后一条?!

3. 推送内容只能是 256 字节。

4. 证书太多了,制作、切换证书太麻烦!

生产环境推送证书、测试环境推送证书、tvOS推送证书、watchOS推送证书、VOIP推送证书。

APNs新闻栏

2014年6月,2014年6月份WWDC搭载iOS8及以上系统的iOS设备,能够接收的最大playload大小提升到2KB。低于iOS8的设备以及OS X设备维持256字节。

2015年6月,2015年6月份WWDC宣布将在不久的将来发布“基于 HTTP/2 的全新 APNs 协议”,并在大会上发布了仅仅支持测试证书的版本。

2015年12月17日,发布“基于 HTTP/2 的全新 APNs 协议”,iOS 系统以及 OS X 系统,统一将最大 playload 大小提升到4KB。

新旧 APNs 协议工作示意图对比

接下来我们分别对新旧协议进行一下介绍:

反人类的旧APNs协议设计

在介绍新版 APNs 前,让我们来吐槽下旧的基于二进制的 APNs 协议设计是多么反人类:

在理论上,推送分发的服务器要打开一个同 APNs 网关服务器的连接,并保持这个连接。但在旧的协议下,APNs 服务却不保证 socket 能维持这个连接。如果通道上没有消息往来,空闲下来到话,socket将被路由掐断。也就是说:APNs 连接说断就断,而你无能为力。有意思的是:在旧的协议下,如果服务器响应成功的话,你将不会收到任何回应,但是如果服务器响应失败(例如,使用了一个非法的 Push token),服务器将返回了一个错误编码,并关闭这个socket。最重要的是,你必须重新发送使用这个无效 token 以后发送的所有推送(详情见示意图)。因此,你可能一直不能确定你的推送是否成功的被 APNs 服务器接收。

成功了不响应,失败了才响应,这个是最大的反人类。于是许多开发者想到了一个很 tricky 的办法:利用这个“漏洞”,比如在每发送10条后故意发送一个错误的token,如果APNs有响应了,就可以确认 APNs 是处在可用状态的,进而确认这10条消息是发送成功的。如果没有响应就说明可能连接已经中断,那么这10条消息很可能是丢失的,然后做进一步的处理。但代价显而易见:将导致你们的推送系统性能低下。苹果有一个名为"feedback"的服务,我们可以定时调用这个服务来获取invalid tokens的列表。这个服务你只要调用一次就可以获得所有的invalid tokens 列表。invalid token越多,你们的推送系统性能将越低。而且 APNs 只要一发生错误就关闭这个连接,然后重新连接。也就是“重启” socket 连接。

示意图:

图中的 PN2 去哪里了?它被放到了 feedback 列表里,等待下次你调用 feedback 服务,然后重发。

为什么Apple要在旧APNs中设计出“重启”的策略?

为了效率。

就像PC机出问题,我们总说“重启能解决90%的问题”。

那么接下来就让我们看看Apple为解决这些问题而推出的基于 HTTP/2 的全新 APNs 协议。

基于 HTTP/2 的全新 APNs 协议

来看下新版的 APNs 的新特性:

# Request 和 Response 支持JSON网络协议

# APNs支持状态码和返回 error 信息

- APNs推送成功时 Response 将返回状态码200,远程通知是否发送成功再也不用靠猜了!

- APNs推送失败时,Response 将返回 JSON 格式的 Error 信息。

# 最大推送长度提升到4096字节(4Kb)

# 可以通过 "HTTP/2 PING" 心跳包功能检测当前 APNs 连接是否可用,并能维持当前长连接。

# 支持为不同的推送类型定义 “topic” 主题

#不同推送类型,只需要一种推送证书 Universal Push Notification Client SSL 证书。

示意图:

其中最大的变化就是基于了 HTTP/2 协议,采用了长连接设计,并提供 “HTTP/2 PING” 心跳包功能检测、维持当前 APNs 连接,解决了老 APNs 无法维持连接的问题。而且新增的状态码特性,也解决了这个问题:无法获知消息是否成功地从你们的推送系统投递到了 APNs 上。理论上,你们可以保证消息是100%投递到了APNs的,因为你可以准确的知道哪条消息到达了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 是复用 TCP 管道连接的,而且 HTTP/2 也以高复用著称,这也使新的 APNs 协议更加高性能。

改进了,但仍需改进。还是有坑

当 APNs 向你发送了多条推送,但是你的设备网络状况不好,在 APNs 那里下线了,这时 APNs 到你的手机的链路上有多条任务堆积,APNs 的处理方式是,只保留最后一条消息推送给你,然后告知你推送数。那么其他三条消息呢?会被APNs丢弃。

有一些 App 的 IM 功能没有维持长连接,是完全通过推送来实现到,通常情况下,这些 App 也已经考虑到了这种丢推送的情况,这些 App 的做法都是,每次收到推送之后,然后向自己的服务器查询当前用户的未读消息。但是APNs也同样无法保证这多条推送能至少有一条到达你的 App。很遗憾的告诉这些App,这次的更新对你们所遭受对这些坑,没有改善。

为什么这么设计?APNs的存储-转发能力太弱,大量的消息存储和转发将消耗Apple服务器的资源,可能是出于存储成本考虑,也可能是因为 Apple 转发能力太弱。总之结果就是 APNs 从来不保证消息的达到率。并且设备上线之后也不会向服务器上传信息。

但Google Cloud Messaging就有这些特性。而且 GCM 现在也支持iOS设备了,那么 APNs 和 GCM 现在就形成了竞争关系。让我共同期待 APNs 在2016年6月的 WWDC 的能有新的改进吧。


推荐相关基于Http2的开源项目

1. https://github.com/CleverTap/apns-http2

A Java library for sending notifications via APNS using Apple's new HTTP/2 API. This library uses OkHttp. Previous versions included support for Jetty's client, however, we've removed that due to instability of the Jetty client.

Note: Ensure that you have Jetty's ALPN JAR (OkHttp requires it) in your boot classpath. See here for more information. This is required until Java 9 is released, as Java 8 does not have native support for HTTP/2.

2. https://github.com/linkedkeeper/apns-http2

A Java library for sending notifications via APNS using Apple's new HTTP/2 API. This library uses Netty4.

Note: This is required until Java 7 is released.


转载请并标注: “本文转载自 linkedkeeper.com ”