說說apns和http2


相信以蘋果爸爸一貫的行事風格,必然在某一天宣布不再支持舊協議,所以各公司都會開始逐漸轉用新推送API,我很難想到會有那么多人私信我一些關於nghttp2的問題,我也是萌新啊,雖然過了很久,想寫一些遇到的問題,記錄一下備忘。
 
人在做一件事的時候反倒腦子不清楚,過后回看,有些做法不合適,有些應該做的沒做。
 
 
僅考慮對接APNs做推送業務,用最不嚴謹的方式,粗略的分幾類:
1.token量百萬以下,推到時間不要求假實時(幾秒內),單appkey
這種是很多小型app的自用要求,一句話,只讓我能推,就ok。
方案:搞一個推單條的client工具,腳本定時跑一下就行了。現在有go的,有node的,有python的
方案2:對接任意一個雲推送sdk(推薦:極光),省心,說不定這個app活不了幾個月。
 
2.token量千萬級別,有一定實時性要求,單appkey,還要有一些常規性質統計數據支撐,比如用戶送達率,token失效率(token失效意味着應用卸載,這個數據對產品狗們很關鍵)
 
這種是典型的一個成熟期app產品的推送場景,當希望產品能在自己把控下演進的公司,會想不開,選擇自研
我們之后主要考慮這種場景,做一個最通用的東西,先起個名,比如說這個東西可以叫apnspx ,px是proxy的意思。它的形態從外面看,是一個單進程http服務,使用jsonapi跟外界通信。
最好不要搞特殊化,做后端6,7年了,唯一一個小經驗就是:
所有同步接口,都應該用http json api,所有異步接口,都要走MQ,如果團隊成熟想上rpc,都應該用grpc,不然的話,事情就會一團糟。
 
3.普適性推送平台,要處理成千上萬個不同的appkey,十億以上海量token,秒級甚至毫秒級送達,還要有詳盡的業務報表和漂亮曲線展示給你老板,用來說明你的app非常牛逼或者快要死了。
 
這是類似極光,雲吧,個推等那幾家做雲推送公司的核心需求。
然而,在這個層面上,apns只是這個復雜系統中的一個小服務,更多制約系統性能的,反倒不是apns本身了。常見的幾個關鍵問題,都是需要在系統層面去解決的。
- 數據總線,系統的吞吐全部需要用消息隊列來異步化
- token失效淘汰,需要有一套常規緩存系統加合理的淘汰算法
- 長連接輪轉,需要有一套分析app活躍性和推送特征的分析系統,反哺后端,指導資源分配
- 億級app客戶的處理,高峰時期的系統伸縮,同上
- 平台是開放的,當然還要有防攻擊防刷的那一整套東西
- 到APNs的連通性,甚至跟代碼無關,快讓你的老大,去跟運維組的老大剛一波正面,讓他們把機房部署在美國或者香港,你看戲就好,保證推送速度飛快。
 
簡化場景3,這些東西想全做在apnspx,是想不開的行為,我假設系統里核心部分是部署在容器化的平台上,只負責推送,和輸出原始統計日志,別的策略由更高層的調度來處理,比如某app出現大推送的動態擴容這種問題。
 
從頭開始作一個小設計,並時刻謹記,只做場景2,不然會陷入無盡選擇恐懼的泥潭,其中為什么這么做我寫一下個人的理解
 
參數傳入時機的權衡,
程序啟動了,帶有幾個初始參數:appkey,證書(現在生產開發也是一個證書了)
評論:之前曾經妄圖運行中動態拉取證書,這樣可以支持多appkey,后來發現,證書的過期,失效與更新是又一個問題,反倒復雜化了。事實上這些邏輯應該做在另一個層面。
細化至少要有如下可配置項:
連接數,連接最大重試數,token失敗最大重試數
 
輸入未動,統計先行。功能本身甚至不如統計重要,服務本身的統計要能從一個/status接口輸出,
最要緊的:
當前批次隊列長度,就緒數,等待數,平均連接重試數,這些可以驗證是否網絡通暢
啟動到目前,推送批次量,推送總量,成功,失敗,重試,各自總量
還有一組維度:每秒,每5分鍾內以上各指標。
這些信息是評判健康的最主要標准,到這個粒度暫時夠了。
 
輸入,一個json,起碼要支持兩種格式,一個payload+tokenlist,一個tokenlist,並且每個token有各自的payload
第一種是推廣告和新聞最常用的,有必要單獨
第二種是每個用戶會有所不同,類似出驗證碼,或者精細化人群推送
也就是說:輸入進來要分好給每個token推什么,細分人群不是apnspx的功能
 
核心的推送引擎,根據實現的選擇有兩種模式,多路復用的無阻塞風格,語言級協程
無論是哪一種,我都 **非常不建議** 使用http2的多路stream特性,除非google給我寫了一個庫,讓我用起來感受不到stream。
一條連接,一路stream,失敗關閉,成功繼續。保持簡單
多路stream,不好用,不適用
就像nghttp2提供的六萬多個回調(其實大概是十幾個,難道我會去數嗎?),作為庫的使用者,應用層開發者,要明白一個事實:這些不是給你用來寫那些掙工資的代碼的,除非你對有更高要求
順帶說一下nghttp2,提供了協議中定義的每一個階段前后的鈎子,目的是給高性能服務器h2o提供最精細的控制能力和手段,同理stream特性也是,是用來給web瀏覽器內核這種級別的項目來用的。
 
http2的本質復雜性,是由於它違反了網絡分層模型,雖然是出於迫不得已。
http2在應用層協議上重新定義了一個連接策略層,然后,再在上面,定義應用層。
如果使用stream,相當於在處理一個微型的tcp協議棧。我覺得不應該在應用開發中,去擺弄消息幀。
 
apns http接口,跟之前最大的區別,就是每一條都有回執,單條失敗不斷鏈,這樣避免了之前的兩個問題:
1.不知道快速發送中哪一條失敗,還需要配合使用response接口。
2.減少連接重建的耗時
至於什么多路復用,什么動態header壓縮,都是添頭,我覺得屬於過度設計,再次認證了“第二版本綜合征”,不用也可以。就當成一個http1的協議來用
 
(“第二版本綜合征”是《unix編程藝術》中一個很有深意的定義:一般一個軟件,第一個版本會功能缺失不堪啟用,第二個版本會試圖解決第一個版本中的所有問題,和作者根據第一個版本用戶反饋的新需求,以及自己臆想的擴大化需求,做出來一個非常臃腫繁雜的版本。然后第三個版本會開始想清楚自己要什么,去掉枝葉,最后演化成真正好用的軟件)
 
 

第一步上來,域名解析的問題,在3那個極端場景里,阻塞的域名解析也不可接受,但要認識到,這個問題是another problem,使用單獨的方案,getaddrinfo調用它就是阻塞的我能怎么辦?我也很絕望啊!系統級的有skydns,小規模的亦可以自己維護一個表,或者無視阻塞,這一步之后在系統中,只考慮ip。

 
之后是網絡處理,socket連接,ssl連接,這一步出錯會得到很多有用信息,好好的詳細返回,一般說明蘋果爸爸認為你的證書有問題了
這個地方,開啟的連接數, 這個參,是一個需要根據實際情況,慢慢調優的,多了少了都不好,跟機器性能網絡速度都有關系,分幾組測試,多跑一段時間,從單進程十來個連接,慢慢往上加,這時候發送速率統計可以指導最后實際生產環境中的最佳值。
 
邏輯上維護兩個線程安全隊列
主線程將數據不斷的push數據進引擎,引擎將結果push進處理完畢隊列,失敗但未達到重試次數的,重新進輸入隊列
最后結果集隊列全部完畢,將結果輸出,結果就是apns返回的那一坨,可以加一些自定義的,比如推送完成時間等。
 
當認為因為偶發服務掛掉等原因導致的狀態丟失不可接受,就使用一種快速的持久化隊列,替代內存隊列,比如redis,或者ssdb。這樣服務本身是stateless的,更好一些。
 
這一批推送完畢,斷開連接。或者延遲斷開,等待下一批,建議最好是主動做斷鏈重連。
推送中斷鏈,重連重試,直到超過自定義次數上限。
 
結果做json序列化,返回。
 
另外再做一個服務或一組腳本,來做其他外圍的事情:
比如處理要喂給apnspx的輸入,比如填入各用戶單獨的信息,分批次,發給多台apnspx的實例,監控apnspx的各項參數,分析每批的輸出,從下批輸入中去掉已經失效的token減少無效推送嘗試,替換證書,等等等等,我一般喜歡用python做這類事情,建議用python做,這個東西會漸漸膨脹成一個怪胎
 
這樣兩個服務單獨演化,保持接口穩定,后端部分專注解決性能問題,外圍部分專注處理那些經常隨業務變化的需求,最后這個warpper即使會變得很大,可能慢慢變成了一套簡化版場景3的系統,但無論如何,仍不影響apnspx
 
使用時候控制每一批的數量,分批多次。一次推得太多,APNs會生氣,認為你在攻擊它,把鏈接關了。
APNs到國內網絡不穩定,可能就斷了。
APNs自己有bug,就斷了
APNs不高興了,就斷了
不知道為什么就斷了,比如海底光纜斷裂,奧特曼擊毀地球等。
不要相信長連接的可用性,在可能失敗的地方都加入重試邏輯。
任何重試都要設定上限,並給出統計結果。
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM