简介java
介绍基于HTTP2实现消息推送苹果APNs的设计思路和架构实现,并讲解采用Netty4 构建 HTTP2 长链接客户端,推送消息到苹果APNs的技术实现。git
简单介绍从 Http一、Http1.1 到 Http2 语义的发展变化,以及 APNs 的相关知识。github
正文chrome
#1 HTTP2浏览器
HTTP1.0最先在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始普遍应用于如今的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为普遍的HTTP协议。性能优化
在 HTTP/1.0 的时候,client每请求一项资源,都必须先创建一次 TCP 连线,而在 client 收到 server 的 response后,便会断开TCP连线。而在 HTTP/1.1 的时代,容许同域名下的资源 request and response 后,才断开 TCP 连线。服务器
HTTP2 是 HTTP/1.1 后的一次重大的改进,在协议层面改善了以上问题,减小资源占用,来,直接感觉一下差别:网络
HTTP/2 is the future of the Web, and it is here!架构
这是 Akamai 公司创建的一个官方的演示,用以说明 HTTP/2 相比于以前的 HTTP/1.1 在性能上的大幅度提高。 同时请求 379 张图片,从Load time 的对比能够看出 HTTP/2 在速度上的优点。并发
HTTP/2 源自 SPDY/2。SPDY 系列协议由谷歌开发,于 2009 年公开。它的设计目标是下降 50% 的页面加载时间。当下不少著名的互联网公司都在本身的网站或 APP 中采用了 SPDY 系列协议(当前最新版本是 SPDY/3.1),由于它对性能的提高是显而易见的。主流的浏览器(谷歌、火狐、Opera)也都早已经支持 SPDY,它已经成为了工业标准,HTTP Working-Group 最终决定以 SPDY/2 为基础,开发 HTTP/2。HTTP/2 标准于2015年5月以 RFC 7540 正式发表。
HTTP/2 的5个特点:
Binary Framing Layer
HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 的文本格式。
在 HTTP/1.X 由 OSI model 中的 Application Layer,存在着 Binary Framing Layer,记录着 HTTP 的内容像 HEADER 中的 method、content、message 等内容,以及 Data 部分。
Frame 的基本格式以下:
一个基础的 Stream 由 HEADER frame 与 Data frame 组成。(共有10种 frame)
而每次的 connection 能够乘载着任意数量的 stream。
能够用 chrome 内部自带的工具(chrome://net-internals/)查看 HTTP2 流量,但这个包信息量比较少,结构不如咱们熟悉的 Fiddler or Wireshark 清晰。
用 wireshark 抓包:
一个包内有多个不一样的 Steam ID
Multiplexing
Multiplexing 容许单一的 tcp 有多重请求/回应,也就是说 client 和 server 能够将 http 请求/回应分解成不藕合的 frame,而后随机发送,最后在另外一端根据 stream ID 将 freame 组合起来。
Request Prioritization
在 HTTP/2 中,stream 有著 priority 的属性,而藉由 Priorty frame,即可以创建起 priority tree。
Header Compression
在 client 和 server 个维护一个 HPACK,採用 hash 的方式来记录 HEADER 的内容。也就是说当 client 要请求资源前会先去 HPACK 查找缺失的资源,接著请求缺乏的资源。
Server Push
在 Binary Framing Layer 提过,frame 共有10种。在这裡会利用 PUSH_PROMISE 的frame,它用于 server 主动发送资源给 client。
#2 APNs
APNs 是 Apple Push Notification service 的简称(注意 APNs 的大小写, s不须要大写)。
反人类的旧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,哪些没到。重发特定失败消息成为可能。
#3 APNs的开源项目
基于 Socket 的 APNs-PUSH
1. https://github.com/notnoop/java-apns
2. https://github.com/RamosLi/dbay-apns-for-java
基于 HTTP2 的 APNs-PUSH
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.
总结
从基于 Socket 的 notnoop 和 dbay 实现 APNs 推送,到基于 HTTP2 的 linkedkeeper 的 APNs 推送,不断深刻了解 APNs 和 HTTP2 等,以及基于 Netty4 构建 APNs-HTTP2 客户端,已在京东 POP 实现业务化运行,从性能和即时性都有明显的提高。在此我向你们推荐一个架构学习交流群。交流学习群号: 744642380, 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良