一個單元化架構的例子

wsydb 8年前發布 | 10K 次閱讀 軟件架構 分布式系統

這是一篇正經文章,原文發在InfoQ,距今正好兩年。本來我寫公眾號是因為世上正經的文章已經夠多,要給大家帶點不一樣東西的。早上跟TimYang聊起背景介紹的事兒,他說先生還是發一下吧。

言歸正傳,本周四414打算在高可用架構群賣個煎餅,其實是聊架構。如果你周四要聽,這篇文章可不要錯過。

這里還是向訂閱的朋友們說聲抱歉,如果乃們覺得這篇文章過于正經,我向組織保證,后面有一堆不正經的文章等著呢啊。

一條正經的分割線

微博粉絲服務平臺在單元化架構方面的實踐已經在QCon講過,這次重又寫起文章,我想傳播知識已經不那么重要(單元化架構不是創新,稍后會詳細介紹),更重要的是還是希望能夠借此引起諸位的思考,能夠在架構層面多投入精力思考和嘗試。

為什么要有架構實踐?

很多人喜歡的是細節,因為有句名言叫魔鬼在細節里,于是都去細節里尋找魔鬼。但是打敗了魔鬼就能看到天使么?未必。細節其實是最容易掌握的部分,細節之外還有很多。就像有了水泥和沙子,你能夠做出混凝土,但是離建成高樓大廈還有很長的路要走一樣,你要學著去設計架構。

但是事情并沒有完,就像沒有唯一的真理一樣,架構也并不是只有一種。你不可能一朝學會,從此天下無敵。如果要賑災,你需要的是帳篷,如果要重建,你需要的是瓦房。不同的住所需要的是不同的架構。

不同的服務也需要不同的架構設計,這也就是我們需要架構實踐的重要原因。在這之后的原因,是我們做任何服務,都要考慮服務的性能和成本。

但優化有很多方式,為什么是架構呢?誠然,從硬件到操作系統,從共享庫到應用軟件,從算法到架構,每一層都可以優化,但每一層所做的工作量和收益也都是不同的。架構可能是需要投入最多精力的,但在很多時候卻也是很少的可以提供超過數量級的提升方式。

所以,思維方式的轉變才是你最應該在意的部分,單元化只是一個例子,而粉絲服務平臺只是這個例子的例子,而已。

言歸正傳,接下來本文將從三個問題來介紹這次實踐,單元化是什么,為什么要用以及我們如何做到的。

1. 單元化是什么

單元化架構是從并行計算領域發展而來。在分布式服務設計領域,一個單元( Cell )就是滿足某個分區所有業務操作的自包含的安裝。而一個分區( Shard ),則是整體數據集的一個子集,如果你用尾號來劃分用戶,那同樣尾號的那部分用戶就可以認為是一個分區。單元化就是將一個服務設計改造讓其符合單元特征的過程。

圖 1 :洋蔥細胞的顯微鏡截圖,單元化要達到的目的就是讓每個單元像細胞一樣獨立工作

在傳統的服務化架構下(如下圖),服務是分層的,每一層使用不同的分區算法,每一層都有不同數量的節點,上層節點隨機選擇下層節點。當然這個隨機是比較而言的。

圖 2 :傳統的服務化架構,為伸縮性設計,上層節點隨機選擇下層節點

與其不同的是,在單元化架構下,服務雖然分層劃分,但每個單元自成一體。按照層次來講的話,所有層使用相同的分區算法,每一層都有相同數量的節點,上層節點也會訪問指定的下層節點。因為他們已經在一起。

圖 3 :單元化架構,為性能和隔離性而設計,上層節點訪問指定下層節點

2. 為什么要用單元化

在性能追求和成本限制的情況下,我們需要找到一種合適的方法來滿足服務需求。在傳統的分布式服務設計,我們考慮的更多是每個服務的可伸縮性,當各個服務獨立設計時你就要在每一層進行伸縮性的考慮。這是服務化設計( SOA )流行的原因,我們需要每個服務能夠單獨水平擴展。

但是在摩爾定律下,隨著硬件的不斷升級,計算機硬件能力已經越來越強, CPU 越來越快,內存越來越大,網絡越來越寬。這讓我們看到了在單臺機器上垂直擴展的機會。尤其是當你遇到一個性能要求和容量增長可以預期的業務,單元化給我們提供另外的機會,讓我們可以有效降低資源的使用,提供更高性能的服務。

總體而言,更高性能更低成本是我們的主要目標,而經過單元化改造,我們得以用更少(約二分之一)的機器,獲得了比原來更高(接近百倍)的性能。性能的提升很大部分原因在于服務的本地化,而服務的集成部署又進一步降低了資源的使用。

當然除了性能收益,如果你做到了,你會發現還有很多收益,比如更好的隔離性,包括請求隔離和資源隔離,比如更友好的升級,產品可以灰度發布等。單元化改造后對高峰的應對以及擴容方式等問題,各位可以參考 # 微博春節技術保障系列 # 中的單元化架構文章,也不在此一一贅述。

3. 我們如何做到

此次單元化改造基于微博現有的業務,因此這里也先行介紹一下。粉絲服務平臺是微博的內容推送系統(代號Castalia),可為V用戶提供向其粉絲推送高質量內容的高速通道(單元化之后已到達百萬條每秒)。整個服務涉及用戶篩選、發送計費、屏蔽檢查、限流控制和消息群發等多個子服務。由于改造思想相通,這里以用戶篩選和消息群發兩個服務為例,下面兩圖分別為商業群發在服務化思想和單元化思想下不同的架構。

圖 4: 服務化思想下的商業群發架構設計(舊版)

圖 5 :商業群發在單元化思想下的架構設計(新版)

對于篩選服務,在服務化架構里,需要去粉絲服務獲取粉絲關系,然后去特征服務進行用戶特征篩選,最后將篩選結果傳輸到群發服務器上;而在單元化架構里,粉絲關系直接就在本地文件中,用戶特征服務也在本地,最后的篩選結果再不需要傳輸。服務本地化(粉絲關系和用戶特征存儲)減去了網絡開銷,降低了服務延時,還同時提高了訪問速度和穩定性,而篩選結果本地存儲又進一步節省了帶寬并降低了延遲。以百萬粉絲為例,每次網絡操作的減少節省帶寬8M左右,延時也從400ms降為0。

群發服務同樣如此。由于在服務化架構里,我們使用 MySQL 和 Memcache 的方案,由于關系數據庫的寫入性能問題,中間還有隊列以及相應的隊列處理機,所有四個模塊都有單獨的機器提供服務,而在單元化架構里,四合一之后,只需要一套機器。當然機器的配置可能會有所提升,但真正計算之后你就會發現其實影響微乎其微。原因除了前面介紹的硬件增長空間外,上架機器的基本配置變高也是一個原因。而且,在單元化方案里,當我們把緩存部署在本地之后,其性能還有了額外的 20% 提升。

一些業務特有問題

不過群發這個場景,我們也遇到了一些特定的問題,一是分區問題,一是作業管理。這里也與各位分享下我們的解決方法。

  1. 分區問題分區問題其實是每個服務都會遇到的,但單元化后的挑戰在于讓所有服務都適配同一分區算法,在我們的場景下,我們按照接收者進行了分區,即從底層往上,每一層都來適配此分區算法。這里有特例的是用戶特征和屏蔽服務,由于總體容量都很小,我們就沒有對數據進行分區,所有單元內都是同一套全量數據,都是一個外部全量庫的從庫。不過由于本單元內的上層服務的關系,只有屬于本分區的用戶數據被訪問到。所以,適配同一分區算法在某種程度上講,可以兼容即可。

  2. 作業管理按照前面的分區方式,將群發服務的整體架構變成了一個類似 Scatter-Gather+CQRS 的方案,因為 Gather 不是一個請求處理的必須要素。也就是說,一個群發請求會被擴散到所有單元中,每個單元都要針對自己分區內的用戶處理這個群發請求。廣播方式的引入,使得我們首先需要在前端機進行分單元作業的處理監控,我們在此增加了持久化隊列來解決。同時,由于單元內每個服務也都是單獨維護的,作業可能在任何時間中斷,因此每個作業在單元內的狀態也都是有記錄的,以此來達到作業的可重入和冪等性,也就可以保證每個作業都可以在任何時間重做,但不會重復執行。

除此之外,我們還對服務器進行了更為精細的控制,使用 CPU 綁定提高多服務集成部署時的整體效率,使用多硬盤設計保證每個服務的 IO 性能,通過主從單元的讀寫分離來提高整體服務等等。

后記

我平時不善文章,現在要成文發表,還是有一點緊張的。不過想到或許可以拋磚引玉,有機會向各位大牛學習,或者跟各位同學一起交流,內心又有些許期待。關于微博或者其他任何網站的設計,歡迎大家一起探討,隨時在微博恭候。

來自:  一樂來了

 本文由用戶 wsydb 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!