騰訊專家10年沉澱:後海量時代的架構設計

圖片

圖片

👉騰小云導讀

移動互聯網後半場,海量技術已經成爲了標配。在架構設計時,開發者能做什麼、要考慮什麼,從而實現一個設計精良的架構?歡迎往下閱讀,和騰訊後臺技術專家呂遠方一起聊架構設計!

👉看目錄點收藏

1 背景

2 架構的邊界

3 架構的組織屬性

4 架構的反饋

5 總結

01、背景

騰訊面向內部開發者的海量服務之道系列課程頗具名氣,它爲司內外海量用戶提供互聯網服務的經驗傳承。無數開發者尤其是後臺技術棧的開發者都獲益於這些課程,從而成長起來。本篇我們將提煉這個核心課程精髓,供廣泛開發者參考。

海量服務的核心是可用性,最終的目的是用高可用性來支撐海量用戶的海量請求。無論是意識、方法論、價值觀還是手段,都體現了對於某個方面的方式方法的高可用性追求。下述是海量服務之道的課程,同時這裏也列出業界的三個最重要分佈式理論:

圖片

最近幾年,大量互聯網產品的體量也逐漸壯大。日活大幾千萬甚至上億的產品不在少數,不一一列舉。業界也湧現了非常多技術,如各種 rpc 框架、docker、雲計算、微服務、Service Mesh、分佈式存儲、DevOps、NoSQL、大數據、各種計算框架等等。

正是因爲這些技術的蓬勃發展,才支撐了越來越大量級的各種互聯網服務公司。反過來說,從移動互聯網發展起來之後,智能終端用戶量的爆發,突破了 PC 時代的互聯網服務的使用場景,互聯網/移動互聯網服務的用戶規模也越來越大,加速了這些支撐大規模互聯網服務公司的技術發展。總的來說,技術發展和實際應用從來都是相輔相成的。

到了移動互聯網發展的後半場,海量技術已經成爲了標配。在架構設計上,到底還能做什麼或者要考慮什麼,才能體現出一個架構的設計精良?這個問題值得思考。

個人理解的後海量時代的架構設計,更應該要考慮的是系統性和內部、外部之間的邏輯關係和複雜度。總結了以下幾個方面(海量服務之道只是列舉部分並非全部能力):

圖片

海量服務的架構都非常複雜,涉及 Android、iOS、H5 等各種端的展現和訪問接入。後端也有接入層、業務邏輯層、基礎層之分。有內存 Cache、分佈式 Cache,NoSQL 和 RDBMS 關係型數據庫,可能還有離線處理部分,有時爲了解耦還會引入 MessageQueue、消息總線等。

不僅分層分級,還會有各種同構或者異構的子系統。這些子系統之間有相互依賴和複雜的調用關係。還會有很多第三方的依賴,有不同類型的接口定義和協議交互方式等等。

怎麼去界定每個模塊的功能定義?每一個模塊和子系統怎麼做到高內聚低耦合以及相互隔離?因此在海量基礎的架構設計中,第一個要考慮的問題就是架構的邊界。

02、架構的邊界

架構邊界要考慮的點如下:

  • 邊界思維、邊界意識,探索邊界、擴張邊界
  • 職責分離、防火隔離
  • 契約精神
  • 高內聚、低耦合、層次分明

舉個例子:在開發一個 App,需要和後臺通過 HTTP 來交互。那麼首先要明確的有以下幾個問題:

  • 協議通信的 layout 怎麼定義?一般分包頭和包體。
  • 終端和後臺有幾次交互,每次交互的請求和返回字段是什麼?
  • 採用什麼樣的協議交互,JSON、JCE、ProtocolBuffers ?
  • 錯誤碼怎麼定義?是否有二級錯誤碼?頭部一個錯誤碼,代表整體的錯誤和異常情況,比如登錄過期等。而包體有錯誤碼定義,標識當前請求的返回情況。

在項目中,終端和後臺是通過 HTTP Post 來交互的,定義了 layout,JCE 來做爲交互協議的具體格式,定義瞭如下 Jce 結構(爲了簡化,這裏只示例請求包體)。注:JCE 是 TAF(騰訊內部使用多年的基於微服務的統一應用框架TAF,Total Application Framework,從2017年開源 ) 框架中客戶端和服務端的通信協議,類似thrift和pb協議。

struct ReqHead {
            Int cmdId;
            ...
}
struct Request {
            ReqHead head;
            vector<char> body;
}
struct PkgReq {
            PkgReqHead head;
            Request request;}

HTTP Body 分爲 3 部分:

Struct Cmd1Request {
}
Struct Cmd1Response {
            Int ret;
}

所有的 Jce 定義全部在一個文件 Protocol.jce,新加的 Cmd 定義也全部在這個文件中。上傳 svn/git,所有終端和後臺開發,都要從唯一的地方去更新或者獲取。Protocol.jce 有 4 個內容:定義 HTTP Body 的交互 Layout、 所有頭部或者公共的 Jce 結構、 所有 Cmd 對應的 Request、Response 結構和所有的頭部錯誤碼定義和枚舉定義。

至此,Protocol.jce成爲終端和後臺有且僅有的唯一邊界,並且按照約定的規範來更新,這成爲大家共同遵守的契約。

後臺服務支持到終端的 TCP 長連接,是更高效的 Request/Response 命令字符交互方式,用於替換 HTTP 形式的交互,同時也方便做後臺到終端的 Server Push。而通過 HTTP 的命令字請求,只支持 Request/Response 的形式。Android 終端的 Google 官方消息推送方案  GCM(Google Cloud Message)無法在國內使用, 不像 iOS 系統,有統一的 APNS。國內廠商都有自己的消息推送通道,比如小米、華爲、Oppo、Vivo、魅族等。

如果終端在線時,後臺可以使用 TCP 長連接通道,直接向終端 Push 運營信息,就可以實時觸達到終端設備。但是終端不在線時,就無法推送。爲了更實時的讓終端用戶收到這些運營信息(例如最新的遊戲資訊),就不得不連接終端手機廠商的消息推送通道。

有些第三方消息推送方案已經支持了上述能力,例如騰訊內的信鴿(騰訊的移動推送服務)等,但是長連接通道已經存在,也無需接入更多的第三方 SDK,若希望後臺對消息通道提供更加可控和更加靈活的消息推送方式,需接入廠商消息通道。

涉及到後臺到終端的消息推送的業務訴求,也比較多:

  • 能支持廠商通道,在終端設備不在線時也能收到消息推送(除非用戶手動關閉消息接收提醒)。
  • 支持定時發送。
  • 支持對所有在線設備羣發消息。
  • 需要對消息的接收能做確認,至少包含在線設備的網絡層發送成功(通過已有的 TCP 長連接通道)、終端設備確認接收成功(終端收到推送消息時通過長連接通道發送 ACK 確認消息到後臺)。
  • 發送失敗的消息,能按照一定的策略重發。
  • 業務方要能查詢發送消息的狀態,並支持條件訂閱。
  • Android 和 iOS 希望能有一套統一的消息推送方式,減少上層業務使用 PUSH系統的成本,並且屏蔽不同終端設備的差異。

從上述業務訴求看得出這是一套邏輯相對複雜的系統,處理也比較複雜,要考慮的因素會比較多。最重要的是要這套系統整合自有長連接通道。值得注意的是 Android 廠商消息通道、iOS APNS,而且 Android 廠商各家通道之間的通訊方式、接口和字段定義都是完全不同的,更不用提 APNS 和自有的長連接通道(自有長連接通道是給命令字設計的)。同時要考慮諸多複雜邏輯如:消息的存儲、終端設備的在線狀態、如果不在線則如何判斷機型信息而選擇對應的廠商通道、通過長連接通道的網絡發送的成功與否、終端設備收到後的消息確認。如果是 iOS 用戶,則要考慮 APNS 證書到期情況等問題。

除了對整個系統做架構設計和模塊拆分之外,開發者還設計了一套通用的 PUSH API,採用 TAF+JCE 的標準 RPC 接口形式。這套接口完全屏蔽上述所有的複雜邏輯,只需按照一套統一的接口方式,提交推送消息就可以,並且業務層可以通過消息隊列訂閱推送消息發送狀態(會預先定義好狀態類型,比如待發發送、發送中、網絡成功、收到確認等)。整體架構圖如下所示:

圖片

PushAPI.jce 成爲整個 PUSH 子系統的對外唯一邊界,非常清晰簡單,並沒有任何其他接口暴露給業務層。業務層也無需瞭解任何內部細節,更不用關心各個廠商通道的差異性,甚至無需關心所推送用戶的設備是 Android 還是 iOS。接口定義如下:

interface PushAPI
{
//單設備推送
int pushSingleDevice(PushSingleDeviceReq req, out PushSingleDeviceRsp rsp);
//多設備推送
int pushMultiDevice(PushMultiDeviceReq req, out PushMultiDeviceRsp rsp);
//全量在線設備推送
int pushAllOnlineDevice(PushAllOnlineDeviceReq req, out PushAllOnlineDeviceRsp rsp);
//全量設備推送
int pushAllDevice(PushAllDeviceReq req, out PushAllDeviceRsp rsp);
};

Push子系統內部實現也體現了高內聚、低耦合的設計原則。 業務層和 Push 子系統職責分離,同時 Push 子系統內部保證高可用和容錯能力,業務層提交給 Push 子系統的消息推送任務,會持久化存儲,不會因爲用戶終端設備狀態異常或者內部處理異常而丟失,真正做到防火隔離。

03、 架構的組織屬性

系統架構和組織架構、團隊分工有關。系統架構和組織架構關聯後,當組織架構邊界和系統架構邊界重合時,要認真對待架構邊界問題,同時對高內聚、低耦合的要求要更高。

有一段時間,後臺邏輯需要依賴另一個團隊提供的接口,後臺全部模塊都是 TAF 服務,對方是 L5(內部使用的名字服務,用來做服務註冊和發現),也是公司內部的一種負載均衡解決方案,可以用 mod、cmd (是L5用來做名字服務的標識)兩個值標識一個服務接口,使用 L5 提供的 C API 來從本機部署的 L5 Agent 裏獲取一個有效 IP、Port,進而進行消息發送。

但是完全無法做爲一種接口調用方式,非常原始和複雜,基本上要調用一個L5 提供的接口,需要做如下步驟:

  • 在要調用 L5 的機器上部署 L5 Agent。
  • 在代碼中通過 L5 的 API 獲取要調用的 IP 和 port,所調用的服務接口由指定的 mod、cmd 兩個參數來標識。
  • 組包,包括 PDU 結構的頭部和包體。
  • 通過 tcp 連接發送。

可以看到這種接口調用方式暴露了太多細節給主調方,調用過程也比較複雜,有以下問題:

  • 每臺主調機器需要部署 L5 Agent,這是 SNG 運維在維護,MIG 運維無法很好的支持。
  • 調用方代碼重複很多。
  • 無法監控和統計成功率。
  • 邊界很模糊,或者說邊界很厚。
  • 查問題很麻煩,因爲 L5 後端的接入層沒有很完善的結果。

爲此,開發者設計了一個轉換層服務 PDUBridgeServer(一個協議適配後臺模塊),實現如下功能和收益:

  • 提供統一 taf 接口給所有需要調用 L5 接口的 taf 服務。
  • 只需要在 PDUBridgeServer 的機器上部署 L5 Agent。
  • PDUBridgeServer 中支持對所有 mod、cmd 所標識接口的監控和告警,會加上一個字符串描述來更直觀的看監控統計數據。

監控統計效果如下:

圖片

當時遇到一個問題,開發者監控到的某個 mod、cmd 接口的異常率高達 10+%,開發者看到的是 PDUBrige 到 L5 的異常率,而對方看的是 L5 到 B 之間的異常率,因爲 L5 本身是有負載均衡策略,根據後端的負載情況會拒絕主調調用,導致 PDUBridge 到 L5 的異常率高,後面開發者通過修改 L5 的參數,異常率降低了很多。我們推測是 L5 的負載閾值比較高,具體是什麼閾值不得而知。因爲這些問題,非常影響效率和團隊關係。

雖然加了一層調用,但是處理非常簡單,只有 ms 級的耗時增加,將 PDUBridgeServer 做爲兩個組織的邊界,通過監控來直觀反應接口調用質量和流量統計,減少了部署成本,也方便 taf 主調服務去調用(直接通過 taf 接口而非 L5 原生接口去調用)。

由以上案例可以看出:架構邊界和組織架構邊界重合時,或者說在考慮系統架構時,要考慮組織的邊界。 例如要有簡單明確的調用接口方式,做好監控和統計、告警,以及系統的整體反饋,大家的認識要一致,確定好統一的目標和衡量指標。例如異常率的定義、成功率等,不能不對等或者理解不一致。這樣才能更好的交流和界定職責和邊界,否則會帶來很多團隊協作問題。例如推諉、爭端等,效率也會極其低下。

04、架構的反饋

架構的反饋是人從系統架構中直接或間接得到的信息,進而去優化和完善架構。 包括但不限於健康度、運行狀況、調用鏈、性能數據、業務運行數據、數據流、日統計、數據趨勢等等。

衆所周知,監控和告警是爲了發現問題、定位問題以及更好地解決問題。但是整個系統只是監控、告警和統計,遠遠不足以反應整個架構的系統性運行狀態。因此,將這些能反映架構運行狀態的所有手段,統稱爲架構的反饋。既然是架構運行的反饋,必然對系統本身的優化和完善,也會有作用。

講個親身經歷,我們在一次重構應用寶 App 搜索和內容搜索時,轉輾了幾個團隊。由於用了開源的 ElasticSearch 解決方案,不能很好的和 TAF 的機制很好的結合,例如自動伸縮、負載均衡和容災容錯等,開發者成員做了不少工作來整合。同時爲了更好的監控搜索系統的運行狀況,和搜索業務的整體情況,例如 Query 分析、刷量等問題,也做了不少監控告警統計。其實這些工作,都是爲了更好的反饋整個搜索系統。

圖片

App 搜索因爲各種利益(刷關鍵詞、刷自己、刷對手等),經常會有刷關鍵詞的情況存在。對後臺來說,如何識別刷量請求、識別後如何處理、應對刷量帶來的突發流量壓力等,都成爲要考慮的問題。一般的做法,識別到刷量請求後(比如明顯的請求特徵 GUID 聚集等)會拒絕請求。「堵不如疏」,在識別到刷量請求後,系統直接從 Cache 中正常返回搜索結果,不走後續複雜的 Query 分析、ES 搜索、召回、排序等耗時環節。這種做法會迷惑刷量用戶,並且在後續的搜索結果曝光、點擊、下載等一系列後續數據上報環節,都會帶上後臺識別出來的刷量標識(會保證刷量標識不會在終端被篡改),在後續數據處理環節也能識別並具備剔除刷量請求以保證上報數據不受影響。

基於 TAF 的 PP 監控(全稱是Property Plus監控。允許用戶通過自定義維度與自定義指標,上報特性, 它由維度名、指標值、以及對應的指標統計方法構成。),開發者做了一個 Query 監控,其能反應 Query 的請求量情況趨勢和耗時對比。某個違禁詞本身在後臺已經被識別爲刷量,並且從下圖可以看出,請求量波動比較大、耗時非常低,正常都在 100 多ms,而這個 Query 的耗時是 2ms。

實際的情況,某個違禁詞是刷量,波動非常大,在時間分佈上也比較集中,因爲識別爲刷量,會命中 Cache,因此搜索耗時非常小。

圖片

大數據 A/B testing 算法對照監控圖

常規意義上的監控和告警、統計,已經無法更好地反映系統整體的運行情況,需要更全面、系統化的方式來反映。同時,也能通過這些體系化的監控統計手段,來回饋架構,對架構的進一步演化提供依據。或者從另一個層面來講,監控和統計被賦予了更多的意義。

架構的演化、平衡之道和架構賦能此處不做展開,如果各位感興趣的話歡迎留言,本欄目會推出相關內容。以上是本次分享的全部內容,歡迎各位開發者在評論區交流。如果你覺得內容有用, 歡迎分享、點贊、在看。

-End-

原創作者|呂遠方

技術責編|呂遠方

圖片

你有哪些架構設計經驗分享?架構設計常見的誤區是什麼?

在公衆號評論區聊一聊你的看法。4月19日前將你的評論記錄截圖,發送給騰訊雲開發者公衆號後臺,可領取騰訊雲「開發者春季限定紅包封面」一個,數量有限先到先得😄。我們還將選取點贊量最高的1位朋友,送出騰訊QQ公仔1個。4月19日中午12點開獎。快邀請你的開發者朋友們一起來參與吧!

關注我並點亮星標

公衆號回覆「架構」領取

OVBU基礎後臺技術總監推薦的架構設計學習資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章