朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

powerzhuye發表於2018-09-22

接上文,繼續剩下的15個模式。

資料管理模式

16、分片模式:將資料儲存區劃分為一組水平分割槽或分片

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

一直有一個說法就是不到沒路可走的時候不要考慮資料庫分片。有的時候業務量大到單個業務表在經過快取+佇列削峰等措施之後的平均的TPS超過1萬,單表實在是扛不住,還是隻能考慮分片手段。

分片前:

· 需要根據資料分佈、壓力情況、業務邏輯確定分片的方式,按照條件還是範圍還是雜湊等等(三個圖展示了三種策略)。

· 需要進行業務程式碼改造,改掉所有不允許的SQL。

· 需要確定用HardCode方式還是框架方式還是中介軟體方式做資料路由。

分片後:

· 需要有運維工具可以對這麼多套分片的資料進行統一的加索引等操作。

· 最好有資料倉儲可以彙總所有資料,使得adhoc查詢可以更方便。

· 最好有輔助工具可以用來幫助定位資料會在哪個分片中。

17、靜態內容託管模式:將靜態內容部署到基於雲的儲存服務,可以將它們直接傳遞給客戶端

相信網際網路公司90%+肯定都使用了這個模式。把靜態資源從動態網站中剝離由Nginx等高效能伺服器來處理靜態資源,然後使用三方CDN對靜態資源進行加速,不但減輕了動態網站的負載而且資料在邊緣節點加速讓使用者的訪問跟快,使用單獨的一個或多個子域名做靜態資源還能提高下載資源的並行度提高網頁載入的速度。

使用CDN來進行資源加速一般有主動資料傳送到CDN儲存和在CDN配置回源站拉取兩種方式,檔案類一般使用主動推送資料,靜態資源類一般使用回源方式。在使用CDN的時候考慮下面的問題:

· CDN以什麼方式來認定同一個檔案的,CDN提供了什麼工具來重新整理邊緣節點的快取?根據不同的策略做相應的快取重新整理方案。

· 源站對於相同的檔案需要有一致性(最好版本變化後檔名變化),不能今天是這個版本明天是另一個版本,這樣很可能導致邊緣節點快取了不同版本的檔案,導致各種怪問題。

· 使用了CDN後不同地區的使用者訪問的都是CDN節點上的資料,一旦出現問題排查比較困難,考慮引入前端的錯誤處理框架來記錄前端出現指令碼錯誤時的呼叫棧,方便定位問題。

18、索引表模式:為查詢經常引用的資料儲存區中的欄位建立索引

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

在第三第五兩篇文章中我都提到了索引表的做法。出於下面的原因,我們會考慮索引表:

· 雖然我們的關係型資料庫大多支援主鍵之外的非聚集索引,但是在某些情況下直接對大表做很多索引效能並不好。

· 做了Sharding後我們確實沒有辦法以分片鍵之外的維度來查詢資料。

· 希望以空間換時間,直接把某個維度的複合查詢作為主鍵單獨儲存一份資料。

不過需要考慮一點索引只有在資料區分度高的情況下才能發揮價值,如果90%以上的資料都是相同的值,那麼走索引進行查詢效能會比全表掃還要差一點。

設計和實現模式

19、前端專用的後端模式:通過使用單獨的介面來分離讀取資料和更新資料的操作

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

這裡說的是不同的前端配以不同的專用後端。比如PC網站和APP的後端是兩套程式。這種模式是否適合其實還是看兩端的後端提供的資料差異有多大,我們總是希望可以儘量統一一套後端,業務邏輯不用重複寫,但是我們要考慮到PC網站和APP的差異性:

· APP系統的介面互動一般會簽名驗證,有的時候還會加密

· PC系統的流程一般和APP系統不一樣

· PC一個頁面能顯示的內容會比APP一個介面顯示的更多

· 安全性設計上PC和APP不一樣,APP很少有圖形驗證碼

考慮到這些差異,我們是在一個工程內根據來源做適配,還是獨立兩套工程來做獨立的後端取決於差異度有多大了。

20、計算資源整合模式:將多個任務或操作整合到單個計算單元中

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

這個模式從資源節省的角度來說我們的計算單元任務可以進行一些合併,減少因為資源限制導致不必要的開銷。

21、選舉模式:通過選舉一個例項作為負責管理其它例項的負責人,來協調分散式應用程式中的協作任務例項集合執行的操作

對於分散式服務,我們趨向於把服務設計為無狀態可以任意擴充套件的,但是在某些業務場景下我們不得不在服務中選舉出一個Leader(Primary節點,Master節點)來做一些不適合重複做的協調管理工作。這個時候我們需要有演算法來做選舉。

最常見的實現方式是使用Zookeeper來實現,我們知道ZK的znode有Sequence和NonSequence兩種,前者多個客戶端只有一個可建立成功同名節點,後者建立後會自動加上序列號命名多個客戶端可以建立多個同名節點,利用這個特性有兩種常見實現方式:

· 非公平實現。多個客戶端同時建立EPHEMERAL+NONSEQUENCE節點。只有一個可以建立成功,建立成功的就是Leader,其它的Follower需要註冊watch,一旦Leader放棄節點(注意,EPHEMERAL意味著Leader待機後Session結束節點被刪除),再一次重複之前的過程註冊節點搶佔成為Leader。這個模式實現簡單,問題是在節點數量過多的時候一旦發生重新競選,這個時候可能會有效能問題。

· 公平實現。多個客戶端同時建立EPHEMERAL+SEQUENCE節點。客戶端都可以建立成功節點,客戶端如果判斷自己是最小的節點則為Leader否則為Follower。每一個Follower都去watch序號比自己小的節點(大家都看前一位)。一旦有Leader節點因為當機被刪除(還是EPHEMERAL特性),收到通知的節點會看自己是不是最小的序號,如果是的話成為Leader。節點當機後,原先watch當機節點的客戶端重新watch比自己序號小的有效節點。這個模式實現複雜,但是由於watch的都只是一個節點所以不會發生像非公平實現的效能問題,而且競選根據節點序號來而不是搶佔式所以顯得Leader的選舉公平有序。

22、管道和過濾器模式:將需要執行復雜處理的任務分解成可以重複使用的一系列單獨的元素

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

在軟體設計模式中過濾器構成的管道這種模式很常見(圖上的業務邏輯就是Handler,之前的那些Task就是Filter,模式上可以是Filter+Handler也可以是Filter+Handler+Filter也可以是Handler+Filter),不管是Spring MVC框架也好,Netty這種網路框架也好都提供了這樣的設計。每一個過濾器單獨完成一個功能,可以獨立插拔隨意組合配置成一套管道,不但資料處理的整個過程清晰可見還增加了靈活性。

對於架構上也可以有這樣的模式,在資料來源進入到業務邏輯處理之前(或之後,或前後),我們可以配置一系列的資料過濾器完成各種資料轉化和處理的任務。Task和Task之間可以是同步呼叫,也可以使用MQ做一定的可伸縮性設計。還可以把過濾器的配置資訊儲存在配置系統中甚至根據上下文動態構建出管道,實現更靈活的前置或後置流程處理。

訊息模式

23、競爭消費者模式:使用多個併發消費者來處理在同一訊息通道上接收的訊息

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

這裡說的是訊息佇列的訊息消費者是一組對等的消費者,通過競爭方式來拉取資料執行。之前提到過這是MQ的最常見的一種模式,一般而言我們會部署多個消費節點進行負載均衡,在負載較大的時候可以方便得增加消費者進行消費能力擴容。不過對於這種模式消費者應當是對等的無狀態的,在某個消費者在消費失敗的時候訊息重新回到佇列隨後可能會被另一個消費者進行處理。

24、重試模式:在應用程式嘗試連線到服務或網路資源遇到預期的臨時故障時,讓程式通過透明地重試以前失敗的操作來處理

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

重試適用於瞬態故障,之後會提到斷路器模式,兩種模式可以結合使用。首先說說重試的幾個發起人:

· 讓使用者自己發起,遇到錯誤的時候及時返回錯誤資訊,讓使用者自己稍後重試整個業務功能。這種方式不容易產生瞬時的壓力,但是體驗較差。

· 在中介軟體自動發起,比如在RPC呼叫的時候遇到服務超時自動進行一定次數的重試,這樣可以在外部沒有感知的情況下有一定概率消除錯誤。這個方式要求服務是支援重試的。

· 由業務邏輯手動發起,不同的業務邏輯根據需求在程式碼中去寫重試的邏輯(當然也可以通過類似Spring-Retry這種元件來做)。實現繁瑣但是不容易出錯。

· 由補償邏輯發起進行同步轉非同步操作,非重要邏輯同步行則行,不行不在主流程重試,由單獨的非同步流程進行重試補償。

重試也要考慮幾種策略:

· 次數。最多重試幾次。

· 異常。遇到什麼樣的異常(黑白名單)應該去重試。

· 等待。考慮每次重試是相同的間隔呢還是有一個延遲的遞增,隨著重試次數增加而增加延時時間。

25、排程、代理、主管模式:在一組分散式服務和其它遠端資源之間協調一組操作

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

這個模式說的是三者的角色:

· 排程負責安排任務,在執行每個步驟的時候維護任務的狀態,具體業務邏輯由代理負責。

· 代理負責和遠端的服務和資源進行通訊,實現錯誤處理和重試。

· 管理者負責監視任務的執行狀態,作為排程的補充,在合適的時候要求排程進行補償。

三個角色相互配合完成複雜的,具有較多遠端服務參與的任務,確保任務的最終有效執行。在之前架構三馬車一文中說到定時任務的時候提到過一種任務驅動表的模式,說到了一些驅動表的實現細節,其實整體和這個模式是類似的思想。當我們的一個複雜邏輯有多個步驟構成,每一步都依賴外部服務,這個時候我們可以選擇全程MQ+補償方式(樂觀方式),也可以選擇全程任務驅動的被動模式(悲觀方式),具體選擇取決於更看重可靠性還是及時性。

彈性模式

26、艙壁模式:將應用程式的元素隔離到池中,如果其中一個失敗,其它的將繼續執行

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

資源隔離有好幾個層次,可以在程式內部做執行緒池或佇列的隔離,在微服務的服務劃分上考慮隔離出單獨的物理服務,或是在伺服器層面通過虛擬化技術或Docker技術進行資源隔離。隔離了就不會相互影響,但是會有成本、效能、管理便利性方面的開銷。實現能夠根據需求分析出可能的資源相互影響的點,提前規劃隔離往往可以避免很多問題的發生。之前有遇到過幾個事故是這樣的:

· 程式內部大量使用了Java8的ParallelStream特性進行並行處理,由於預設共享了相同的執行緒池,某一個業務的執行佔滿了執行緒影響了其它業務的正常進行。

· 訊息佇列因為沒有對執行過多次失敗的死信訊息和正常的新訊息進行隔離,導致一些業務下線後無法處理的死訊息佔滿了整個佇列,正常訊息無法消費。

· 某個服務提供了類似檔案上傳的重量級操作,也提供了資料查詢的輕量級操作,在上傳業務大的時候服務的執行緒都被IO所佔滿,導致其它查詢操作無法進行。

27、斷路器模式:連線到遠端服務或資源時, 處理可能需要花費時間來修復的故障

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

分散式應用環節多網路環境複雜,如果遇到依賴服務呼叫失敗的情況我們或許可以進行重試期待服務馬上可以恢復,但是在某些時候依賴的服務是徹底掛了而不是網路故障無法及時恢復,如果不考慮進行熔斷的,可能服務呼叫方會被服務提供方拖死。這個時候可以引入斷路器機制,如圖所示斷路器一般採用三態實現,瞬間恢復可能會讓底層服務壓力過大:

· 關閉:出現錯誤的時候增加計數器

· 開啟:計數器到達閾值開啟斷路器,直接返回錯誤

· 半開:超時後允許一定的請求通過,成功率達到閾值關閉斷路器,操作還是失敗的話還是進入開啟狀態

實現模式的時候考慮下面注意點:

· 考慮熔斷後怎麼來處理,熔斷後我們肯定拿不到實際的處理結果,這個時候考慮是功能降級還是採用後備的資料提供方來提供資料

· 緊急的時候需要人工介入,最好在外部提供手動的方式可以干預斷路器的三態

· 不同的業務考慮不同的斷路器開啟閾值,每一個錯誤還能有不同的權重,比如對於下游程式返回了太多請求的錯誤,每次錯誤可以+2提高權重儘可能早斷路

· 斷路器應當記錄熔斷時的請求原始資訊,在之後必要的時候可以進行重放或資料修復工作

· 注意設定好外部服務的超時,如果客戶端超時比服務端短,很可能進行錯誤的熔斷

實現上我們可以看一下Netflix的Hystrix進行進一步瞭解。

28、事務補償模式:撤消通過一系列步驟執行的工作,它們一起定義最終一致的操作

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

這個模式說的是失敗時必須進行撤銷的操作,可以由一組補償程式來做相應的補償。在這裡我想說的更廣一點,在服務呼叫的時候,呼叫失敗有幾種可能:

· 請求客戶端發出但是沒到服務端,業務邏輯沒有執行

· 請求客戶端發出服務端收到也處理成功了,業務邏輯執行了客戶端沒收到結果

· 請求客戶端發出服務端收到但處理失敗了,客戶端沒收到結果

所以在出現服務呼叫失敗或超時的時候,服務端執行究竟有沒有成功客戶端是不明確的(只有客戶端收到了明確的服務端返回的業務錯誤才真正代表執行失敗),這個時候需要有補償邏輯來同步服務端的執行狀態。如果這樣的補償不可避免而且需要補償的服務特別多,這樣的邏輯逐一來寫是一件很煩的事情,我們可以把這個工作封裝成一個補償中介軟體來處理:

· 所有關鍵服務呼叫標記為需要自動補償

· 補償中介軟體在資料庫記錄服務的呼叫狀態

· 關鍵服務的提供者提供統一狀態查詢介面,消費者提供統一的補償回撥介面(來處理成功和失敗的情況)

· 補償中介軟體根據資料庫的記錄呼叫服務提供方的狀態查詢和服務消費方的補償回撥介面進行補償

這樣,我們在服務呼叫的時候就不需要考慮補償邏輯的實現,只要實現這個標準即可。

安全模式

29、代客金鑰模式:使用向客戶端提供對特定資源或服務的有限直接訪問許可權的令牌或金鑰

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

這個模式說的是,在訪問敏感資源的時候,我們可以不必讓應用程式在其中作為一個代理轉一層做許可權控制,而是生成一個金鑰,讓使用者直接拿著金鑰到資源池換資料。

一些CDN在提供資源上傳下載服務的時候一般都會提供類似的安全策略,需要實現生成Token才能去使用下載和上傳服務,避免了CDN資料被非法利用作為圖床的可能。

實現上比較簡單,應用程式和資源提供方約定好Token的生成演算法,對資源+請求資源的時間+金鑰聯合在一起做簽名,資源提供方如果校驗到簽名不正確或Token過期或資源不匹配都將拒絕服務。

30、聯合身份模式:將認證委託給外部身份提供者

朱曄的網際網路架構實踐心得S1E8:三十種架構設計模式(下)

這個模式說的是將身份驗證委託給專門的程式或模組來做。使用專門的模組來統一負責登入授權不僅僅可以提供單點登入的功能,而且服務實現上更簡單不需要每次都考慮登入那套東西。實現上可以看一下Spring Security實現的OAuth 2.0。

總結一下,對於其中的很多模式,我們可以發現其實在之前的一些介紹或多或少有一些涉及。這裡提到的30種模式有些體現的是一些設計細節,有些體現的是一種設計理念,它們大多時候是組合使用的,適合的就是最好的,大家可以細細品味一下每種模式的適合場景,在合適的時候可以想到它或許會有一種豁然開朗的感覺。


相關文章