前言
冰凍三尺非一日之寒,葵花寶典也不是一天寫出來的,系統設計也如此,好的架構是不斷演進的。
一般來說能用單塊架構解決的問題,盡量不要采用分布式。
分布式雖然可以提高系統的響應能力,也帶來了更高的復雜性,如果團隊技術人員水平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
- 統計數量是個問題
- 不能再使用事務