朱曄的互聯網架構實踐心得S1E7:三十種架構設計模式(上)前端
【下載本文PDF進行閱讀】算法
設計模式是前人經過大量的實踐總結出來的一些經驗總結和最佳實踐。在通過多年的軟件開發實踐以後,回過頭來去看23種設計模式你會發現不少平時寫代碼的套路和OO的套路和設計模式裏總結的相似,這也說明了你悟到的東西和別人悟到的同樣,通過大量實踐總能趨向性得出一些最佳實踐的結論。架構設計也是同樣,這裏結合本身的理解分析一下微軟給出的雲架構的一些模式。話說微軟幹這方面的事情真的很厲害,以前翻譯過的《微軟應用架構指南》寫的也很不錯。有了模式的好處是,技術人員和技術人員之間的對話能夠絕不費力的經過幾個模式關鍵詞進行交流,就像如今你們溝通提到職責鏈模式,若是雙方都理解這個模式的意義那麼這五個字替代的可能就是半小時的解釋。廢話很少說,接下去來看一下這些其實已經很熟悉親切的模式。數據庫
進程外的代理服務(以前介紹中間件的時候也提到了,不少框架層面的事情能夠以軟件框架的形式寄宿在進程內,也能夠以獨立的代理形式作一個網絡中間件)。這裏的大使模式意思就是這麼一個網絡代理進程,用於和遠端的服務進行通信,完成下面的工做:後端
因爲是獨立進程的網絡服務,因此這個模式適合於咱們有多語言多框架都須要幹一樣的事情,那麼咱們的框架中客戶端部分的不少工做能夠移出來放到大使服務中去。固然了,多一層網絡調用多一層開銷,大使服務的部署也要考慮到性能不必定能夠集中部署,這些都是要考慮的問題。設計模式
使用一層防腐層來做爲新老系統通信的中間人。這樣新系統能夠徹底使用新的通信方式和架構方式,老的系統又不用進行特別改造能夠暫時保留,等老系統不用以後能夠廢棄這個反腐層。這種模式適合新老系統遷移的過渡方案,不屬於永久使用的架構設計模式。緩存
這個模式說的就是能夠有一個外部的配置服務來保存配置信息,在以前第五篇文章介紹中間件的時候我詳細說明過配置服務的功能。無論是處於管理運維的角度仍是方便安全的角度,具備配置共享配置外存特色的獨立配置服務對於大型的網站來講必不可少。實現的話有不少開源項目提供了配置服務,見以前個人文章。安全
應用程序若是須要和多個服務交互的話,在中間構建起一個聚合網關層,網關併發發出多個請求給後面的服務,而後彙總數據給到應用程序。這種模式有幾個好處:性能優化
固然,使用這種模式須要考慮到網關的負載、高可用、高性能(異步IO)等等。服務器
其實這種模式不只僅用於純後端服務之間的通信,不少面向前端的API請求都會作一個聚合層,這樣前端能夠只發一個請求的狀況下任意向後端一次性索取多個API的返回,減小網絡請求次數提升性能。網絡
實現上最簡單的方式可使用OpenResty或Nginx實現。
名字有點難以理解,其實這種模式咱們可能一直在用。就是用一個代理網關層作一些和業務無關的又麻煩的點,好比SSL,實現上用Nginx實現就很簡單。咱們常常會對外啓用HTTPS服務,而後對內服務實際提供的是HTTP接口,經過網關作一下協議轉換。
這也是很常見的做法,咱們對外的接口多是/cart、/order、/search這樣的API,在其背後實際上是不一樣的服務,經過網關層進行轉發,不只僅能夠作後端服務的負載均衡和故障轉移,在後端服務變動切換對外API路徑(好比版本升級)的時候咱們也能夠進行靈活的路由,確保了對外接口的一致性。可使用Nginx來實現,相信大部分公司都是由Nginx這樣的網關來對外的,不會把域名直接解析到底層服務上對外。
這個模式實際上是挺重要的一點,有幾個點須要注意:
實現上,咱們應當把health端口做爲插件形式集成到系統,配置一下便可啓用,用不着每個系統都本身開發一套。若是使用SpringBoot的話能夠直接使用Actuator模塊實現。
名字挺嚇人,這個模式說的是如何作遷移。經過創建一個門面來做爲後端新老服務的路由,慢慢把服務替換爲新服務,最後當全部的服務都是新服務後刪除這個門面便可。這樣對於消費者感知不到這個遷移的過程。在上一篇文章中咱們提到的換引擎的方式其實說的是保留原有的門面,也是經過這個門面作底層引擎的替換。其實我以爲對於減小外圍影響這種模式是徹底能夠理所固然想到的,真正難的過程仍是以前說的數據遷移和底層服務實現的過程。
這個模式說的不是廣義上的緩存使用,而是其中的一種使用方式。咱們對於緩存的使用通常有這麼幾種方式:
這個模式說的是後一種方式,對於數據變更不大,這種模式是性能最好的,幾乎實現了100%的命中率,並且若是數據量不大能夠容納進進程的話不須要跨進程通信。往細緻一點去想,這裏還有一層性能優化的點,由於咱們在內存中維護了一套複雜的全量數據的數據結構,內存中對象的引用只是指針引用,內存中的數據搜索能夠很快,對於數據量不大可是關係複雜的數據,這個搜索效率能夠是數據庫的幾百倍。實現上通常會在應用程序啓動的時候把數據徹底加入內存,在後續經過一些策略進行數據更新:
英文縮寫是CQRS,看到這個關鍵字你可能會以爲有點熟悉了。CQRS原來講的是咱們能夠有兩套數據模型分別用於讀和寫。好處是,咱們可讓讀和寫具備徹底不一樣的數據結構,減小相互的干擾,減小權限控制的複雜度。這裏說的不必定是指架構層面咱們能夠這麼作,也指在程序內部,咱們能夠有兩套命令模型來處理讀寫這兩個事情,分別進行優化和定製。
如今通常的作法是相似於上圖的作法,爲讀寫配置兩套獨立的數據源,而且和事件溯源的方式結合起來作(見後面一節)。咱們來講說讀寫兩套模型在存儲上分離這個事情,在《相輔相成的存儲五件套》一文中咱們的架構圖其實就有這方面的意思。對於讀寫這兩個事情,咱們徹底能夠不用一套數據源,爲讀創建專門的物化視圖,能夠針對讀進行優化,避免在讀的時候作不少Join的工做,能夠把性能作到極致(後面會有物化視圖模式的介紹)。事件溯源+CQRS+物化視圖三者通常會結合起來使用。
事件溯源(ES)是一種有趣的模式,說的是咱們記錄的不是數據的當前狀態而是疊加的數據變化序列(是否是想到了區塊鏈的數據記錄方式)。傳統的CRUD方式由於有更新這個操做,因此會產生性能併發方面的侷限性,並且咱們還須要配備額外的日誌來作審計,不然就產生了信息丟失。而事件溯源模式記錄的是事件而不是當前狀態,因此有下面的特色:
其實有一些業務場景下這種模式會比CRUD存儲更適合:
反過來講,業務邏輯很簡單的系統,須要強一致性的系統,數據不多更新的系統不適合這種模式。不知你所瞭解到的採用ES模式的業務場景有哪些?你們一塊兒交流一下。
咱們在使用數據存儲的時候每每會更多考慮存儲而不是讀取。咱們使用各類數據庫範式來設計數據庫,在讀取數據的時候咱們須要作大量的關聯查詢以輸出符合須要的查詢結果。這個時候性能每每會成爲瓶頸,物化視圖是一種空間換時間的作法。與其在查詢的時候作關聯,倒不如提早保存一份面向於查詢和輸出的數據格式。所以,物化視圖適合下面的場景:
可是由於須要考慮到物化視圖計算保存的開銷,因此也不太適合於數據變化太頻繁的狀況,由於數據加工須要時間,因此不適合須要數據強一致性的場景。
實現上通常是基於消息監聽作額外維護一套物化視圖的數據源和主流程解耦。惠普的Vertica是一款高性能的列式分析數據庫,它的一個特性就是物化視圖,經過事先提供SQL語句直接緩存面向於統計的查詢結果,極大程度提升了性能,也是空間換時間的思想。
消息隊列咱們太熟悉了,以前咱們也反覆提升過好屢次,甚至我說這是架構三馬車之一。這個模式在這裏強調的是削峯的優點。這裏我還想提幾點:
區別於FIFO結構的隊列,優先級隊列容許消息標識處理優先級。這裏實現上如上面兩個圖有兩種方式:
在方案選擇和實現上要考慮消息優先級是否須要絕對按照優先級來處理,仍是說相對優先處理便可,若是須要絕對優先那麼除了消息位置重排還須要有搶佔處理。還有,若是咱們採用第二種多池的方式來處理的話可能會發生低級別的消息處理時間比高級別的消息更快的可能性(若是二者處理業務邏輯是徹底不一樣的話)。
實現上的話RabbitMQ 3.5以上版本支持了消息優先級,實現的是第一種方式,在消息有緩衝的堆積的時候進行消息重排,消費端能夠先看到先處理優先級高的消息,這種方式在消費速度大於產出速度的場景下是沒法實現高級別消息優先處理的。
補充一點,對於隊列中的消息,還有一種須要特別考慮的就是一直停留在隊列的消息應當視爲低優先級或是死信消息來處理,最好是有單獨的消費者來處理,避免此類消息影響了整個隊列的處理,見過不少個事故是因爲隊列中被廢棄消息卡死致使完全喪失處理能力的。
在作壓力測試的時候咱們會發現,隨着壓力的上升系統的吞吐慢慢變大並且這個時候響應時間能夠基本保持可控(1秒內),當壓力突破一個邊界後,響應時間一會兒會不可控,隨之系統的吞吐就會降低,最後會完全崩潰。任何系統對於壓力的負荷是有邊界的,超過這個邊界以後系統的SLA確定沒法知足標準,致使你們都沒法好好用這個服務。由於系統的擴展每每不是秒級能夠作到的,因此這個時候最快的手段就是限流,只有限流了才能保護如今的系統不至於突破這個邊界完全崩潰。對於業務量超大的系統搞活動,對關鍵服務甚至入口層面作限流是必然的,別無它法,淘寶雙11凌晨0點那一刻也能看到必定比例的下單被限流了。
常見的限流算法有這麼幾種:
令牌桶算法限制的是平均流入速度,容許必定程度的突發請求,漏桶算法限制的是常量的流出速率用於平滑流入的速度。實現上,經常使用的一些開源類庫都會有相關的實現,好比google的Guava提供的RateLimiter就是令牌桶算法。
限流模式有下面的一些注意事項:
在下篇中咱們將會繼續介紹數據、安全、消息、彈性方面的一些架構模式。