百萬在線直播互動平臺基于Docker的微服務架構實踐
原創【51CTO.com原創稿件】本文從具體的項目實例出發和大家討論如何從無到有地去搭建一個能夠快速伸縮的微服務架構。
2017 年 12 月 1 日-2 日,由 51CTO 主辦的 WOTD 全球軟件開發技術峰會在深圳中州萬豪酒店隆重舉行。
本次峰會以軟件開發為主題,新浪微博研發中心平臺高級系統研發工程師溫情在微服務與容器技術專場帶來了主題為“基于 Docker 的微博直播互動微服務架構”的精彩演講。
本文將圍繞以下主題,探討直播互動的微服務架構設計:
- 針對現在非?;馃岬闹辈鼍?,如何設計一個穩定的消息互動系統,以支持***消息的互動分發。
- 在服務越來越復雜的情況下,如何對系統進行合理地組件化拆分,進而實現以微服務的方式進行獨立部署和升級。
- 在混合云的體系下,如何充分利用 Docker 技術和公有云的彈性特性,設計一個基于混合云的彈性化的架構,以實現在流量突增且無人看守的情況下,自動完成服務的彈性伸縮。
微博直播互動平臺的背景與挑戰
2017 年可謂直播大年,新浪微博在自己的 App 推出了直播服務,同時也打通了與淘寶、一直播、紅豆等平臺的互動。
從技術上說,直播一般分為兩個部分:
- 視頻流,包括流媒體的傳輸部分。
- 直播互動,包括評論、點贊、送禮等其他部分。
直播的特點是:由于房間里人數眾多,直播平臺需要能夠支持百萬用戶同時在線的場景。
微博直播平臺特點
直播互動系統的基本模型是一個消息轉發的系統,即某個用戶發送一條消息,其他用戶收到該消息,并執行相應的存儲。
消息系統一般具備三個基本的評判標準:實時性、可靠性、和一致性。對于直播這種消息系統而言,它對于可靠性和一致性的要求并不高,而對實時性的要求卻非常高。
如果用戶給主播送完禮物 10 秒鐘或者 1 分鐘之后,主播才能收到并回復。這是不可接受的。因此我們需要具有“秒級”的實時性。
微博直播互動的特點:
- 平時的流量不太大,但是當訪問多的時候流量會產生明顯的猛增。
- 流量的激增表現在消息推送量的巨大。一般可以達到每秒千萬條消息推送。
- 對于一般短連接的請求而言,用戶為了進入界面僅發送一次請求,后續通常不再產生其他操作,因此服務器端的壓力不太大。
而直播互動場景則不同,在用戶加入房間之后,就算不做任何操作,也會收到一大堆的推送。這種實時性高的推送方式會給服務器造成持續的壓力。
微博直播平臺面臨的挑戰
由于用戶對直播的需求量大,且玩法多樣,我們面臨了來自三個方面的挑戰:
- 如何快速迭代并快速地響應需求,這是對業務以及系統的衡量標準。
- 如何應對全量 Push 所引發的流量峰值。
- 如何實現成本***化,即如何運用有限的資源應對波峰波谷的交替。
微博直播互動平臺微服務架構演進
針對上述三大挑戰,我們自己搭建了直播互動的微服務架構。上圖是直播互動的架構圖,我們對業務層進行了模塊化的劃分,包括發現服務、三方平臺服務、推送服務等各種微服務。
架構選型
在架構選型方面,我們先來看傳統的單體模式。單體適用于許多場景,特別適合于敏捷開發。
由于微博互動的流量龐大,且系統相對復雜,因此會面臨如下問題:
- 上線成本高,迭代速度慢。單體模式是將所有的服務集中在一塊兒,實現一起部署和一起上/下線。因此任何一次細微的改動,都需要我們重新“上線”。
- 而為了掃清可能出現的服務問題,我們一般會進行全量回歸測試。可見這種緩慢的迭代速度是我們所無法接受的。
- 可伸縮性差。在服務進行水平擴展時,隨著服務增多,Redis 和 DB 的連接數也會逐步增多,而每個資源都有一定的連接數瓶頸,當服務增長到一定數量之后,則會導致服務無法進一步地水平擴展。
魯棒性差。系統中任何一處的 Bug,都會導致其他的服務,甚至是整個服務的癱瘓。
而微服務與單體之間的區別在于:微服務會將一些共同功能予以拆分,并且在拆分的基礎上,每個模塊都能維護自己的 DB 與資源。
因此微服務的好處體現在:
- 每個服務都被獨立地開發和部署,迭代速度明顯提高。我們不必測試全部,只需測試自己所開發的服務便可。
- 單個服務所依賴的資源變少,且擴容的速度明顯提高。特別是對于一些輕量級的服務,我們能夠保證在 30~40 秒內,完成擴容并啟動。
- 通過 RDC 或者相關的調用,移除一些推送服務對于 DB 和 Redis 的直接依賴,從而解決連接數的問題。
- 將各個服務獨立進行部署,從而避免了那些無關聯的服務問題所引起的整體宕機,將 Bug 限定在單個模塊中。
微服務化難題
微服務的引入也帶來一些新的問題:
- 以前在單體模式中,我們將各個模塊雜糅在一起,不必詳細考慮。如今采用微服務了,應當如何進行合理的拆分呢?
- 以前只是本地調用,現在改成了微服務,它們將如何通信?如何考慮網絡開銷呢?
- 在服務部署之后,微服務如何去發現問題呢?
難題一:服務拆分
對于微服務的拆分,大家最為關心的問題是拆分的力度。我們采取了如下簡單的方式,大家可以在自己的項目中稍做借鑒:
- 不知如何拆分時,可“一刀兩斷”地直接拆分成核心和非核心服務。其好處是對服務做到相應的隔離。
例如:在服務器不夠用時,我們只保護核心的服務,而非核心服務不會影響到核心服務。因此是一種根據業務重要性的拆分方式。
- 服務治理,可以隨時對非核心服務予以降級。我們讓有限的機器資源去支撐核心的服務。
完成了基礎拆分之后,我們就可以根據業務場景對核心服務進行進一步的拆分:
- 針對 Short Service,我們有一個叫做 Wesync 的私有協議解析。由于我們平時并不會修改其代碼,而且該協議的解析具有通用性,因此我們將其拆分出來單獨進行部署。
- 我們將所有與業務相關的服務,如房間和消息等,全部放置在一個叫 Biz Service 的服務中進行部署。這一部分的代碼量會非常大,但是代碼量并非服務是否拆分的衡量標準,由于其處理復雜業務的壓力并不大,因此可以將它們歸納到一起。
- 將 Push Service 這一推送服務進行拆分,它可以維護與用戶之間的長連關系,從而保證消息推送的實時性。另外服務端“推”的方式會比用戶端“拉”的方式更具有實時性。
我們對于 Push Service 進行拆分的原因在于:
- 由于消息的推送量巨大,Push Service 成為了整個服務的瓶頸點。假想在一個 10 萬人的房間中,如果 1 秒內有 10 個人群發了消息到客戶端上,那么系統會產生 100 萬條每秒的推送量。
同理,如果房間里有 100 萬人,則會產生 1000 萬的推送量。可見該消息量的量級是非常大的,因此 Push Service 是我們需要頻繁擴容的服務。
- 我們在設計時會希望 Push Service 盡量簡單,并讓它對資源減少依賴。
我們簡單地從業務角度對非核心服務進行了如下拆分:
- Barrage Service:包括直播回訪功能和獲取歷史消息。
- Third Service:與三方平臺的接入服務。
我們來看 Barrage Service 的拆分原因:由于該服務是一種用來批量獲取歷史消息的對外暴露式服務,那么批量地獲取歷史消息必然會帶來大量的帶寬消耗。
因此,我們需要采取如下的優化方式:
- 我們采用 Gzip 的壓縮技術對接口和返回的數據進行壓縮,從而減緩對于帶寬的消耗。
- 充分利用 CPU 和其他硬件的性能。
- 增加多層緩存的策略,我們可以直接在本地 load,而不必去 DB 或 message cache 里 load,因此減少了與外界的交互。
如上圖所示,我們在進行微服務化拆分之前,各個服務被雜亂無章地雜糅在一起,我們不得不一起部署。因此,我們根據各種策略,按照上述方法進行了服務的拆分。
難題二:服務通信
同時我們也從同步與異步的角度,對服務之間的通信進行了拆分:
- 同步操作:REST 的 API 方式+RPC 方式。
- 異步操作:主要圍繞的是消息隊列。
對于核心服務和非核心之間的通信交互,我們進行了場景分析。如上圖所示,第三方服務(Third Service)包含兩個方面:
- 藍線部分表示第三方服務器推送(Push)消息到我們的系統。由于我們希望第三方來的消息能夠更為實時地(同步)展現到我們 App 的前端,因此采用的是 RPC 類型的 Push 方式。
- 紅線部分表示我們從微博 App 產生消息,然后對外傳遞給第三方服務器,讓他們以 Pull 的方式來獲取消息。由于我們不希望其他的服務、后面的服務、以及非核心服務影響到我們的核心服務功能,因此我們實現的是異步解耦,即采用 Queue 類型的 Pull 方式。
對于 Barrage Service,我們采取的是共享數據庫的方式。由于批量獲取回放消息是一項大量消息帶寬的服務,因此我們共用一個數據庫,通過直接從庫里 load,以保證系統資源的提供。
難題三:服務發現
對于 Push Service 類型的服務發現,我們棄用了以前掛在 DNS 處讓 DNS 做服務發現的方式,而是采用了自己寫的 Dispatch Service。
它的運作機制是:
- 為了在用戶加入房間之前建立一個長連的過程,我們發送一個 Dispatch 與 Push Service 建立相應的連接。一旦有消息產生,則通過 Push Service 進行推送。
- Dispatch Service 可以動態地去根據用戶所屬的服務器和區域,按照策略就近做出相應的選擇。
- 該策略可以支持 Push Service 的水平擴容。即在擴容之后,我們不再給老的 Push Service 推送流量,也不再返回相應的 IP,而是把全部流量導入新的 Push Service。
而對于 RPC 類型的服務發現,我們采用典型的 SOA 架構,包括:Registry 提供可用服務列表、Server 提供服務、Client 發現并使用服務。因此,我們采用的是 Motan RPC,以快速地響應各種需求并完成發現。
微服務化總結
總結起來,微服務解決了如下四方面的問題:
- 獨立開發測試,加快了迭代的速度。
- 通過服務拆分,減少了無關聯服務之間的影響。
- 通過減少對資源的依賴,提高了服務擴展的速度。
- 當然也增加了服務的部署和運維的難度。
既然采用了快速擴容的框架,那么我們就需要運維同學的參與和部署。下面來討論直播互動的彈性擴縮容策略。
微博直播互動平臺的彈性擴縮容
基于 Docker 的混合云架構
由于對微博的使用是一個典型的流量激增場景,因此如果采用盲目購置服務器這一傳統的應對方案的話,會造成整體負載飽和度的不均衡。
例如,大家一般在白天和半夜都不會去“刷微博”,服務器和網絡的利用率會比較低。只有在晚高峰出現時才會有爆炸式負載的產生。
因此我們采用了快速彈性擴縮容的應對策略,即利用公有云端的各種快速彈性伸縮的資源服務。
但是,由于之前的私有云環境是在我們自己完全掌控的范圍內,而如今引入的公有云則帶來了環境上的差異性問題。于是我們參考業界的普遍方案,采用了 Docker。
Docker 的網絡模型選擇一般有 Bridge、Container 和 Host 等實現方式:
- 我們起初在測試環境中采用了 Docker 的默認設置 Bridge 模式。
- Docker 在通過 Daemon 啟動時會有一個 Docker0 的虛擬以太網橋。
- Docker 的 Daemon 運用 veth pair 技術進行虛擬化,即在容器內與宿主機之間建立虛擬網卡連接,而在容器外進行相應的消息轉發。
- 存在的問題:由于容器內使用的是虛擬 IP 地址,我們就使用該虛擬 IP 注冊了 RPC 服務。但是在啟動之后,Client 端卻出現無法真正訪問到該虛擬IP。
因此我們采用了 Host 模式,即:
在 Host 模式下的同一個 eth0 可以被共用,因此各方能夠共享宿主機的網絡命名空間。
同時由于它省去了各種路由的開銷,因此會比 Bridge 模式更快。
可見,Docker 的優點在于簡單輕便,非常適用于微博的應用場景。另外,再加上公有云端的一些資源,共同構成了基于 Docker 的混合云架構,我們稱為 DockerDCP。
值得一提的是 DCP 已經開源了,在 GitHub 上有一張 Open DCP 的服務圖,大家可以去搜索一下。
DCP 的作用是能夠在 10 分鐘之內擴容并部署 1000 臺機器,以應對諸如“三大節日”的流量猛增。
因此,它每天都會有著 6000 億次 API 的調用,以及萬億次的 RPC 調用。
為了讓直播互動與 DCP 實現相關的自動化運維部署與擴縮容,我們每次都會將消息推送至 SLB(負載均衡),通過 Push Service 的推送服務來實現跨網服務的部署。
要想實現擴容首先要獲知設備的來源。DCP 能夠幫助我們區分內網和外網(公有云)不同的機器。
例如:內網的三個業務方— A、B、C 都有自己的多臺服務器,而我們將它們設置為一個“共享池”。
業務 C 會因為峰值流量而申請池中的 3 臺服務器,而在業務空閑時則將資源歸還到池中。如此,我們可以自由地在私有云和共有云上實現快速擴容,即為“雙云擴”。
自動化擴縮容流程
我們直播互動的自動化擴縮容流程大致分為:
- 制定監控的指標,即設定達到何種監控指標的閾值,才開始擴容操作。一般是通過壓力測試來獲取到。
- 監控指標的采集,包括如何進行采集,以及采集哪些指標。
- 數據流轉到容量決策系統,以決定是否進行擴容。
- 一系列服務擴容的標準流程,如上圖所示。
而縮容的流程與上述擴容流程較為類似,在此就不贅述了。
對于上述提到的監控指標,我們分為兩大類:
- 業務性能指標,不同的業務之間會存在著差異,有的 API 服務能夠支撐 1000 個 QPS(Query Per Second),而有的卻只能支撐 200 個。因此根據業務的不同,我們所采集和監控的性能指標也不盡相同。
- 機器性能指標,側重的是通用化的機器性能指標,包括帶寬、PPS、CPU、性能、IOPS 等。只要發現哪一塊出現了瓶頸,就應當去盡快擴容。
相對應地,指定監控指標的流程為:對性能系統進行相應的壓力測試>發現服務的瓶頸點>對瓶頸點進行分析>制定監控的指標。
如今我們也正在嘗試著通過機器學習來實現自動化監控。我們一般會每周或是定時對各種服務進行壓力測試,以及時地去發現服務的瓶頸。
由于新引入的機器可能會導致整體性能的不一致,而且隨著服務需求和代碼量的增多,整體服務的瓶頸點也可能會相應地遷移到其他地方,因此我們通過進化版的壓力測試,實現了對瓶頸點的實時把握。
就 Push Service 的指標監控而言,我們在壓力測試時所監控的業務性能包括:
- 用戶數據的長連數。因為單臺機器可能會撐起幾千個用戶長連數。
- 消息推送量。在某些的業務場景中,可能長連數并不多,但是其消息推送量卻非常大。因此我們需要從不同的維度實施監控。
而對于機器性能的指標,同樣會包括帶寬、PPS、CPU、內存、IOPS 等。
就監控指標的采集而言,我們分為兩個方面:
- 業務的性能指標是由各個業務系統負責相應的采集。
- 機器的性能指標是由運維監控服務執行統一的采集。
彈性擴縮容總結
總結起來,我們在彈性擴縮容方面實現了如下三點:
- 通過容器技術 Docker 化的服務解決了環境差異性問題。既實現了更快速擴縮容,又讓整個虛擬化更為標準。
- 通過混合云架構 DCP 解決了資源彈性伸縮的問題。
- 在架構搭建之后,通過自動化擴縮容實現了直播無人看守的場景。
典型案例分享
下面是在實現擴縮容架構之前與之后的兩個直播案例的對比。
未實現自動化擴縮容時,我們曾做過對神州飛船回收的直播。由于發生在凌晨 3 點多、且各方人員并未被通知到,因此我們在不清楚會有多少觀看流量的情況下采用了全量 Push。
通過次日的事后分析,我們發現:服務的流量已觸及瓶頸點,出現了許多的報警,幸好有人員值班,所以并未出現故障。
從維護團隊過于疲憊和服務保障的角度出發,我們決定開始著手實施自動化擴縮容。
如上圖所示,這是我們在實現了自動化擴縮容后的一次直播。左側圖中藍線的每一次波谷代表一次擴容操作。
在波谷處于最小長連數時,由于自動擴容出了一堆機器,因此那一時刻并無流量的進入,連接數基本為零。
之后連接數隨即迅速上升,服務在 30 分鐘之內做了 4 次快速自動擴容。而這些自動化擴容對于運維人員來說都是透明的,只是在擴縮容時會有郵件提醒而已。
溫情,新浪微博高級系統研發工程師,從事微博視頻和通訊相關系統的研發。當前負責微博直播消息互動系統的研發,推崇高可用,可彈性伸縮,低耦合的微服務架構設計。技術上擅長消息通訊方向,針對系統應對突增流量和高并發方面有豐富的實踐經驗。
【51CTO原創稿件,合作站點轉載請注明原文作者和出處為51CTO.com】