架構設計經驗雜談

原則

做一件事情,總得有個原則,它可以幫助我們進一步評價幾個相差不多的事物。架構設計時的原則是什麼?種豆得豆,種瓜得瓜,你秉持的原則直接決定後續的成效。

我的答案是:

  • 可用第一
  • 可維護第二
  • 其它第三

可用性第一。這個沒有什麼爭議。系統再NB,跑不起來,完不成預定需求也是扯淡。

可維護第二。是爭論比較多的一個地方。而我覺得,可維護真的非常非常重要。一段無法維護的代碼就是一顆定時炸彈, 而你卻不知道其爆炸時間。對於團隊來說,如果以犧牲可維護性爲代價,去死扣一點性能而引入過於複雜的算法、組件是非常危險的, 看似贏得了一時,卻輸掉了以後。代碼肯定有bug,團隊即將面對出問題無法調試的時刻。

此外,可維護性一定要與自己團隊實力結合起來。可維護這個事情評估起來非常難,因爲團隊彼此有差異。 某項具體設計或實現的難度,對一個團隊沒有問題,對另一個團隊則可能完全無法維護,只能使用。 而作爲設計者,則一定要把握好這個尺度,自己的團隊能承受多大難度的問題,沒有什麼特殊問題,千萬 不要盲目的因爲別人的隻言片語就霸王硬上弓。

只要代碼可維護性良好,即便他有其它方面問題,隨着時間的推移,肯定會被fix掉。而沒有維護性的設計則沒有生命,出生即死亡,以後只能拋棄掉重構。

在團隊中,我一直反對團隊自己些php擴展解決一些問題。因爲相關工作無人維護,寫完,爽完,就真的完了。這是很恐怖的,當後續問題 出現時,當初寫的人可能離職了,或者無能力完成新的修改,這,都是致命的事情。 而公司有一定規模後,各司其職,有專門的團隊(是團隊,不是個人)來做這件事情時,就OK了。

容忍一個缺點

容忍缺點,似乎不應該讓這麼一個不斷追求完美的人羣聽見。

但是,如果你找不到一個合適的點妥協,那設計永遠是設計。

高可用、無單點、高性能、數據一致性、實時性等,不要妄求在設計中解決一切問題。 要清楚,CAP原則是無法同時實現的,千萬不要讓自己鑽到牛角尖中。

容忍一個問題,說不定會讓你豁然開朗,從而快速完成架構其它部分的設計。

沒有不出問題的架構,當我們想不出更好的方案時,就包容它,轉而爲其尋找出問題時候的快速修復方案。除非,你的時間是無限的,公司允許你一直這樣的想下去,試驗下去。

年初(2014)時我需要設計一個通用的異步服務,服務本身系統保證所有的任務都能收得到。但網絡與系統,總是會出問題的。 沒有系統敢宣稱自己100%的在線率,google牛逼吧,在china照樣無法正常使用,這裏就扯到另外一件事情——做高可用 時,不要把眼光侷限在服務本身。

爲了保證高可用,接收到所有的消息,需要做重試、災備切換等很多事情,但線上服務等着急用呢,另外,災備切換太廢機器。 最後,選擇了客戶端調用時,如失敗則打日誌,後續收集重新請求的方式來解決。這樣,server-client的實現都簡單了很多。 當然,如此妥協,與這本身是一個異步服務有關,其它服務不能照搬方法。

緩存

緩存是所有“讀操作”性能問題的通用解決方案,它是爲程序運行服務的,不是爲工程師服務的,因爲他會增加代碼的複雜度。架構裏面有兩個概念非常關鍵:一是緩存,二是分層。緩存主要解決性能問題,分層主要解決耦合(複雜性)問題。 引入緩存的代價是邏輯複雜性的上升,必須去維護緩存關係。

緩存的常見使用方式有寫時同步構建、讀時按需構建、異步構建三種,各有各的好處。 對於QPS比較高的接口,慎用讀時按需構建,因爲這麼做很有可能會被緩存構建過程卡死。 對於數據一致性要求比較高的場合,慎用異步構建,以防上下文相關的業務邏輯異常。

衡量緩存價值的主要指標是緩存利用率,具體多少利用率合適,需要根據業務實際情況而定。

分層

分層和緩存正好相反,它簡化了業務的複雜性,使其更易被人類理解,就像給一部長長的小說編排了一個清晰的目錄。但它卻降低了程序的運行性能。 它是兩類問題的標準答案:

  1. 邏輯複雜性。
  2. 各種適配問題(更確切的答案,應該是增加中間層)。

引入分層,必然會遇到層次之間如何通信的問題。通信分爲兩類:內部調用與rpc。

內部調用,適合業務邏輯還不是太龐大的階段,一般通過各種庫(lib、動態、靜態庫)來完成,整個系統在操作系統層面還是一個程序。

網絡RPC,一般代碼級別的分層無法解決問題時,我們便會着手進行更徹底的分層,將程序分爲多個獨立存在的程序,彼此間通過網絡進行通訊。網絡rpc會碰到協議問題,這時候應該首選HTTP。選擇HTTP能爲以後節省相當多的配套工具開發時間。不同層次之間通訊還會碰到同步與異步的問題。同步容易理解,但容易被卡住。異步增加複雜性,卻提高了性能。之前語言層次沒有很好的解決這個問題,同步和異步需要自己在業務層解決,而golang卻在語言層解決了,用異步的模式構建語言實現基礎,工程師卻仍然用同步的方式來寫程序,堪稱偉大。

分層會帶來性能問題。 這個時候可以在分層體系的上層增加緩存來解決;或者通過對後端數據的併發訪問來緩解,不過對後端數據的併發訪問會對邏輯表達造成不利的影響,而且使用場景有限,因爲大部分邏輯都是串行爲主的,未必能準確的預測出可以併發獲取的數據是哪些。

此外,分層還需要解決循環調用的問題。

數據分區

分區算法有兩類:按嚴格遞增的區間,或者按照hash。

按照區間,可以非常靈活的處理後續增長問題。但在某些業務場景下帶來舊區間冷數據過多的問題。可以通過定期合併舊區間來解決。

按照hash,比較好的解決了冷熱分佈不均的問題,但在擴展區間時簡直就是噩夢(緩存類的數據到可以通過一致性hash來解決,但持久數據就沒這麼幸運了)。

如果數據量非常大,先hash再在每個hash段裏分區存儲,可以緩解兩者的弊端,但也不是最終完美的解決辦法,最終都會走到通過導數據(手動or自動)來調整分區的路子上來。

發佈了36 篇原創文章 · 獲贊 19 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章