前言
冰凍三尺非一日之寒,葵花寶典也不是一天寫出來的,系統設計也如此,好的架構是不斷演進的。
一般來說能用單塊架構解決的問題,儘量不要採用分散式。
分散式雖然可以提高系統的響應能力,也帶來了更高的複雜性,如果團隊技術人員水平hold不住的話,反而會產生更多問題,例如問題難以定位、系統效能下降、某種業務實現困難或無法實現等問題。
以下內容由偉大的詩人chenqionghe整理,light weight baby~
一、常用指標
響應時間
直觀反應系統快慢,一般控制在200ms以內,超過1s使用者已經能感覺到慢了
併發數
同時處理請求的數目
QPS
每秒查詢請求數
TPS
每秒執行事務數,著重反應寫
二、奪命三高
沒錯,就是高血壓、高血糖和高脂血,開玩笑啦~
高併發
通過設計讓系統能接收更多的使用者併發請求,承擔更大的流量。
一般考查併發數、QPS和TPS。
示例:廬山百龍霸,併發百龍
高效能
一般指服務響應時間快
- 使用者視角。APP、瀏覽器上能直觀感受快
- 開發視角。響應延遲低,系統吞吐量大,併發處理能力強
- 運維視角。基礎設施配置高,CPU多核心,記憶體容量大
示例:雅典娜之驚歎,三位黃金聖鬥士將自身的究極小宇宙集中在一點進行攻擊
高可用
系統通過設計,減少停工時間,保持服務的高度可用性。
一般會用SLA協議衡量服務可用性,以達到幾個九做為標準
以一年為例,1年 = 365天 = 8760小時
- 99.9 = 8760 * 0.1% = 8760 * 0.001 = 8.76小時
- 99.99 = 8760 * 0.0001 = 0.876小時 = 0.876 * 60 = 52.6分鐘
- 99.999 = 8760 * 0.00001 = 0.0876小時 = 0.0876 * 60 = 5.26分鐘
SLA提供的可用性越高,那麼一年內停機的時間越小
示例:雅典娜之驚歎,分成不同的小組放招
三、常見招式
分流
本質就是將流量分攤到不同的節點,負載均衡。
常用方法有nginx、haproxy、traefik
舉例:星巴克開分店,增加營業員、擴大面積
快取
將熱點資料先快取起來,先從快取中獲取,提高效率
例如:Redis快取、Memcached快取、模板引擎快取、CPU快取
舉例:提供超市熱賣攤位,提高顧客購買效率;早餐店先提前把早餐做好,顧客來直接取
佇列
-
提高響應速度。
未處理完成前提前返回,提高響應速度,處理完後再發通知。 -
系統解耦
例如一個下單的資訊需要同步多個子系統,每個子系統都需要儲存訂單的資料的一部分,如果靠訂單服務的團隊維護所有子系統同步,耦合太大,這時候可以通過釋出訂閱模型,訂單服務在訂單變化時傳送一條訊息到一個主題中,所有的下游子系統都訂閱主題,這樣可以每個子系統都可以獲得訂單資料。 -
緩衝流量,削峰填谷
為了避免大量的請求衝擊後端服務,可以使用訊息佇列暫存請求,後端服務按照自己的處理能力,從佇列中消費,例如秒殺、埋點場景。
簡單地說,就是業務上游佇列緩衝限速傳送,業務下游佇列緩衝限速執行
秒條場景,一般處理兩種方式:
加鎖。比如golang包中的mutex,也可以利用redis本身操作原子性的特點
寫入訊息佇列。在訊息佇列中做減庫存的操作
舉例:去海盜蝦飯吃飯,先結賬,做好了給你端過來
CDN
CDN(Content Delivery Network)官方定義叫內容分發網路。
簡單的說就是一種快取,原理是將靜態的資源分發到多個地埋位置伺服器上,最終達到就近獲取資料的效果,例如北京地區訪問北京的資料,海南訪問海南的。
當然,這也不用我們自己開發,例如阿里雲、七牛雲等知名雲廠商都提供了CDN服務。
一般使用就是設定CDN回源更新資料的地址,將服務域名解析到雲廠商返回的CNAME上。
舉例:京東購買東西,發貨都直接從最近的倉庫發貨,只有倉庫沒貨了才會到源頭取貨(回源)
池化
一般連線的建立是比較耗資源和時間,一般我們可以使用連線池來提升效率,這就是傳說中的池化技術,常見的有資料庫連線池、執行緒池。
設定空閒連線數和最大連線數,步驟一般如下:
- 當前連線數小於空閒連線數,建立
- 連線池中有空閒連線直接使用
- 沒有空閒連線,當前連線數小於最大連線數,建立
- 達到或超過最大連線數,按設定超時時間等待舊連線釋放,超時丟擲錯誤
本質都是空間換時間,一般建立的連線物件會放到一個佇列中。
擴容
-
垂直擴容
升配置,例如加CPU核心、加記憶體、改為IO優化型儲存
示例:倍化之術 -
水平擴容
直接加機器,多多益善
示例:影分身之術
熔斷
當某服務呼叫的時候,如果返回錯誤或者超時次數超過一定閾值後,後續請求不再傳送直接返回錯誤
舉例:就像電路的熔斷器一樣,電流過載,自動斷開電路。
開源方案有:hystrix、traefik、istio
限流
通過限制到達系統的併發請求數量,保證系統能正常響應部分使用者的請求。超過限制的流量,通過拒絕服務的方式保證整體系統的可用性。
舉例:十一假期去莫高窟旅遊,景點只放出有限的門票,門票賣完,新來的客戶不再接待。
可以在系統中埋下限流的程式碼,例如可以使用golang的緩衝channel實現。
降級
就像被沙加剝奪了五感一樣
例如雙十一的時候,開啟淘寶,會發現介面上的資訊少了很多,其實這就是一種降級,關閉或者拒絕很多不重要的功能,節省伺服器資源抵禦高併發大流量。
分層
好處:分工明確,方便複用、容易針對層做擴充套件。
這個分層,可以指程式碼架構,也可以指服務架構,一般不跨層呼叫
-
MVC
控制器(C)呼叫模型(M)取資料,再通過(V)渲染檢視。
業務邏輯一般寫到模型中進行復用,但是可能會帶來的是模型之間的職責劃分不明確。
所以一般還會在其中加入Service層,使Model模型不再存放業務邏輯。 -
Web、Service、Dao
- Web:表現層。可以簡單理解成Controller和View
- Service:業務邏輯層。業務邏輯都封裝到這一層,這一層直接呼叫Dao取資料
- Dao:資料訪問層。負責訪問資料庫,最常見的是AR模型或者ORM
可以簡單理解成MVC加了一層Service,Controller直接呼叫Service,Service再呼叫Model
-
Web、Service、Manager、Dao
在Service和Dao之間加了一層Manager,抽取service層之間的共同邏輯。
部署分級
根據優先順序的高低將服務部署到不同的物理機上,可以通過K8S的label選擇最終部署的節點
日誌監控
- 日誌追蹤。
使用ELK或者阿里雲日誌服務。請求和打日誌傳遞requestId,查詢根據requestId檢索請求相關的所有日誌 - 呼叫鏈追蹤.
開源方案Zipkin,Jaeger 。核心是通過TranceId和SpanId追蹤每次呼叫 - Prometheus監控
把需要監控的指標儲存到prometheus中,通過grafanan展示 - Sentry監控
統一搜集採集異常日誌,針對500這種錯誤到sentry後臺查詢,比較方便定位問題
查詢優化
簡單地說是可以走索引,像射箭一樣直中目標
- 優化sql索引。分析sql執行效率,通過加索引優化
- 引入Elasticsearch。提高搜尋效率,降低模糊搜尋給資料庫帶來的壓力。
讀寫分離
將讀請求和寫請求分推到不同的例項,例如MySQL讀寫分離、Redis讀寫分離
MySQL主從分離核心是binlog,主庫將binlog寫入relay log檔案,從庫過來拉取。
主從同步容易遇到延遲問題,例如主庫已經寫入了,從庫查詢的還是老資料,一般會通過以下方式解決:
- 直接讀主庫
- 更新主庫前寫快取,讀快取
- 直接將更新的資料傳遞,不查庫
分庫分表
一般會配合服務一起拆分
- 垂直拆分。
專門的服務使用專門的庫。例如一個購買流程,可以拆分為商品庫、訂單庫。 - 水平拆分。
例如將users拆成10個庫,users0、users1...users9,根據某個欄位的取模存放到不同的庫。
缺點:
- 無法做join
- 統計數量是個問題
- 不能再使用事務