架構師談基於HTTP2推送消息到APNs

簡介

介紹基於HTTP2實現消息推送蘋果APNs的設計思路和架構實現,並講解採用Netty4 構建 HTTP2 長連接客戶端,推送消息到蘋果APNs的技術實現。

簡單介紹從 Http1、Http1.1 到 Http2 語義的發展變化,以及 APNs 的相關知識。

正文

#1 HTTP2

架構師談基於HTTP2推送消息到APNs

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推送消息到APNs

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 在速度上的優勢。

架構師談基於HTTP2推送消息到APNs

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 的文本格式。

架構師談基於HTTP2推送消息到APNs

在 HTTP/1.X 由 OSI model 中的 Application Layer,存在著 Binary Framing Layer,記錄著 HTTP 的內容像 HEADER 中的 method、content、message 等內容,以及 Data 部分。

架構師談基於HTTP2推送消息到APNs

Frame 的基本格式如下:

架構師談基於HTTP2推送消息到APNs

一個基礎的 Stream 由 HEADER frame 與 Data frame 組成。(共有10種 frame)

而每次的 connection 可以乘載著任意數量的 stream。

架構師談基於HTTP2推送消息到APNs

可以用 chrome 內部自帶的工具(chrome://net-internals/)查看 HTTP2 流量,但這個包信息量比較少,結構不如我們熟悉的 Fiddler or Wireshark 清晰。

用 wireshark 抓包:

架構師談基於HTTP2推送消息到APNs

一個包內有多個不同的 Steam ID

架構師談基於HTTP2推送消息到APNs

Multiplexing

Multiplexing 允許單一的 tcp 有多重請求/回應,也就是說 client 和 server 可以將 http 請求/回應分解成不藕合的 frame,然後隨機發送,最後在另一端根據 stream ID 將 freame 組合起來。

架構師談基於HTTP2推送消息到APNs

Request Prioritization

在 HTTP/2 中,stream 有著 priority 的屬性,而藉由 Priorty frame,便可以建立起 priority tree。

架構師談基於HTTP2推送消息到APNs

Header Compression

在 client 和 server 個維護一個 HPACK,採用 hash 的方式來記錄 HEADER 的內容。也就是說當 client 要請求資源前會先去 HPACK 查找缺失的資源,接著請求缺少的資源。

架構師談基於HTTP2推送消息到APNs

Server Push

在 Binary Framing Layer 提過,frame 共有10種。在這裡會利用 PUSH_PROMISE 的frame,它用於 server 主動發送資源給 client。

架構師談基於HTTP2推送消息到APNs

#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 連接。

示意圖:

架構師談基於HTTP2推送消息到APNs

圖中的 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 證書。

示意圖:

架構師談基於HTTP2推送消息到APNs

其中最大的變化就是基於了 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 實現業務化運行,從性能和即時性都有明顯的提升。


分享到:


相關文章: