關於實時推送系統的那點事

無痕幽雨發表於2018-03-02

出處:

http://mp.weixin.qq.com/s?__biz=MjM5NTU2MTQwNA==&mid=401006254&idx=1&sn=d69a0efc740e04ddefe3cdca14d56eb0&mpshare=1&scene=1&srcid=0302M9o0GjqZL27d4bRUBB1X#rd 



於小波,系統架構師,2011年加入魅族,主要從事服務端後臺開發工作,專注於系統高併發,分散式等解決方案。


直播實錄關於實時推送系統的那點事


大家好,我是於小波,2011年加入魅族,現在在魅族移動網際網路部門,主要負責服務端後臺架構設計和開發工作。


很感謝ChinaUnix給的這個機會,非常榮幸可以和大家在這裡分享我們魅族的一些技術。下面,我們進入今天的主題:關於實時推送系統的那點事。




今天的內容主要分4個方面:系統介紹、架構設計&微服務、踩過的坑&心得、監控和灰度釋出。重點介紹一下第三點,也就是一個心得分享。




我們先介紹一下這個系統。




魅族推送系統主要為魅族使用者提供以下服務:系統&應用升級、查詢手機、聯絡人同步、應用商店、線上音樂、閱讀、遊戲中心等,這裡就不一一列舉了。




我們實時線上使用者是2500W左右,日PV 50億,在現有資源的情況下,推送速度最快可以到600W/分鐘。




這個是我們的系統架構圖。


從邏輯上劃分了4層,最下面的是接入層,為使用者提供TCP長連線的接入和http服務。


第二層是訊息分發層,主要功能是上行業務訊息的分發到各個service,下行推送訊息路由到使用者所接入的接入伺服器,再由接入伺服器傳送到指定的使用者。路由表就是用來儲存使用者的長連線資訊和所在的接入伺服器的位置。Webservice的功能後面會提到。


第三層是業務邏輯層,主要處理不同的業務邏輯。


第四層是儲存層,儲存使用者的離線訊息和訂閱訊息。


還有兩個比較獨立的監控平臺和服務管理。


這個系統的是由很多小的服務,每一個服務功能都比較單一,而且是獨立的叢集,可以單獨部署。這裡的服務都是非同步無狀態,要求高併發訊息處理延遲低於1ms。


還有一個推送平臺,不在今天的討論範圍。


我們在開發這套系統的過程中,碰到了很多問題,下面列了幾個比較典型的問題和大家一起分享。




首先,是微服務的問題:


因為所有服務都要求高效能,所以我們開發了一套RPC框架 在魅族內部叫kiev。kiev碰到了兩個問題:


1、同步呼叫

最開始我們這套框架都是同步呼叫,使用簡單,服務的開發效率高。可是隨著使用者量的增多,效能已經滿足不了我們的要求。而且同步呼叫,我們為了提高效能使用了多執行緒,很多多執行緒的問題隨之而來。於是我們改進了我們的框架,使用非同步。


2、非同步問題

非常多的回撥函式,一套完整的業務邏輯被打散在各個回撥函式來實現,程式碼的可維護性差,開發效率也不高,而且還有一個很突出的問題,我們在專案中使用了redis、mongodb、mysql的lib庫,而這些庫都是同步的,如果要做成全非同步 那工作量會非常大。後面我們參照go語言用C++實現了一個協程版本的kiev,hook系統的IO呼叫 比如 send,recv等,把這些系統呼叫改成非同步,達到的效果就是同步的呼叫,非同步的效能。




我們碰到的第二個問題就是手機功耗問題。主要有兩個點:


1、手機流量消耗




這裡就涉及到選擇怎樣的協議,傳統的方式就是XMPP和sip 這兩個協議是純文字協議,非常多的開源元件,能夠快速的搭建一套系統,但是這兩個協議都是網際網路時代的產物,非常消耗流量。協議本身也非常複雜、冗餘,單標準文件就是幾十頁。


為了降低流量,我們的系統使用的是自定義的二進位制協議,可以高度定製,編解碼的速度是上面兩個協議的10倍以上,流量節約了50%-70%。


2、手機電量消耗


因為我們是tcp長連線服務,手機端為了保持這個長連線需要定期的傳送心跳來維持。


一般的做法就是固定3分鐘或者5分鐘發一次心跳。因為傳送心跳需要喚醒手機,如果心跳訊息太頻繁 就會導致電量的消耗比較大,如果太久發一次心跳又沒法保證連線的穩定性。


所以我們根據不同的網路情況 指定了一套智慧心跳模式,根據當前的網路情況來設定傳送心跳訊息的間隔。




還有我們有一個延遲推送的策略。其實很多訊息對實時性的要求並沒有那麼高,比如說系統升級的推送,使用者早幾分鐘或者晚幾分鐘收到升級的推送並沒有多大的影響。針對這種情況,我們對於實時性要求不高的訊息可以在手機處於喚醒狀態才推送,那問題來了,服務端怎麼知道手機是喚醒的呢? 其實很簡單,收到使用者的心跳包,再推送訊息。


第三個問題訊息重複問題。


行動網路的特點是不穩定、高延遲。服務端傳送訊息給客戶端,客戶端收到訊息返回應答,如果應答返回失敗了,服務端沒有收到這個應答怎麼辦?




1、超時重傳 這裡就需要伺服器儲存每條訊息的狀態那麼服務端的邏輯就會非常複雜


2、等下次客戶端連線上來之後再重傳。


不管是怎麼樣,這條訊息都會重新傳送,就導致客戶端收到重複訊息。


解決辦法:改進訊息流程如下圖。




當客戶端有訊息的時候只傳送一個通知告訴他,然後客戶端自己上來拉取訊息,當然需要告訴服務端從什麼地方開始拉取。所以客戶端需要儲存最近收到的訊息最大的序列號。


這個流程的好處就是客戶不會拉取到重複訊息,而且服務端不用儲存每條訊息的狀態,真正做到了業務無狀態。


第四個問題,行動網路的DNS問題。




運營商的DNS服務是很不靠譜的,經常當機,延遲也很大,還容易被劫持。我們採用了全IP的接入方式。


具體就是客戶端通過http服務拉取接入層的IP列表,然後選擇一個IP直連。


這裡訪問http服務的時候也可能會被劫持,我們使用預埋IP的策略,優先使用域名訪問,域名不可用使用預埋的IP訪問。


第五個問題,海量連線的負載均衡問題。




我們單臺接入伺服器可以承受400W的長連線。如果使用LVS來做負載均衡肯定是不行的,首先LVS存在單點問題。


我們解決的方式:




1、在客戶端獲取IP列表的時候,其實就是已經排序過的,負載低的伺服器IP排在前面。


2、客戶端跑馬策略,客戶端隨機選擇幾個IP 同時傳送探測包 哪個IP響應快就用哪個IP,這裡服務端需要一個策略就是收到探測包 需要根據自己的實際負載情況決定是否延遲返回。這個跑馬策略解決了負載均衡的同時也解決了跨運營商網路訪問慢的問題。




關於監控:我們的系統是由很多的小的服務構成,每個服務都是單獨的叢集,如果叢集中一個服務出問題,並不會影響整個業務的使用,但是如果這種問題累計起來,最後可能會導致系統不可用。


所以,只是單純的依靠簡單的業務監控,很難發現未來可能出現的系統故障,所以 我們需要一套嚴格的監控體系來發現潛在的問題。我們針對每一個服務都定義了一些強監控指標。比如:




最後說一下灰度釋出,灰度釋出非常重要,線上的很多問題都是釋出引起的,我們為了降低釋出的風險,引入了灰度釋出。灰度釋出流程如下圖:




在沒有灰度釋出之前我們開發人員都是凌晨才敢釋出,所以我們的狀態就像下面這張圖。




有了灰度之後再也不用熬夜釋出了。




好了,今天的分享就到這裡結束了,感謝大家的支援,如果大家有什麼問題的話,我們可以直接在這裡一起探討,也可以加我微信私下探討。

【互動篩選】

Q1:灰度是什麼?

A1:灰度是介於黑白之間的,平滑釋出。

Q2:重複訊息的問題,可否客戶端儲存最近接收成功但應答失敗的訊息id,重複推送的訊息客戶端直接忽略?

A2:這個客戶端不知道應答傳送失敗了。

Q3:推送的到達時間和一次到達率能達到多少?

A3:推送到達時間300ms以內可以到達。到達率理論上是100% 因為我們有離線訊息儲存,推送不成功的訊息會下次再推送。

Q4:移動端的訊息推送,和PC端的訊息推送,區別在哪兒?實現的難點又在哪兒?

A4:移動端有儘量少的功耗( 電量和流量),pc端不用考慮這個問題。而且行動網路非常不穩定。

Q5:接入伺服器均衡,負載高低度量指標是什麼,連結數還系統級資源負載,或者其他的?

A5:連結數是其中一個指標。

Q6:如果移動端三天或三十天未上線,服務端要儲存這麼久麼?

A6:不會,我們離線訊息有超時機制,一般都是7天。

Q7:關於預埋ip策略,老師能講解下嗎?

A7:預埋IP就是在手機端寫死IP地址,如果需要變更 直接推送新的IP地址。

Q8:關於灰度釋出這項技術魅族有打算開源嗎?

A8:目前還沒有計劃。

Q9:還有一個非技術問題,設計數百萬規模的推送系統,由於公司不太自信,是購買商業產品還是自研好?

A9:如果你們有100W使用者了 當然是自己做,如果還沒有 可以用一些開源的。有100W使用者了說明你們的產品很好,當然要做的更好,吸引更多的使用者。


相關文章