高并發(fā)架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)
| 導(dǎo)語(yǔ) 高并發(fā)解決的核心問(wèn)題是在同一時(shí)間上有大量的請(qǐng)求過(guò)來(lái),然后我們的系統(tǒng)要怎么抗住這些請(qǐng)求帶來(lái)的壓力惊完。本文從基礎(chǔ)設(shè)施層僵芹、服務(wù)端架構(gòu)層、服務(wù)應(yīng)用層分別做了一個(gè)簡(jiǎn)單的梳理小槐,在每一層通過(guò)什么的方式去抗并發(fā)拇派,給大家提供一個(gè)思路
一荷辕、高并發(fā)的說(shuō)明和背景
高并發(fā)解決的核心問(wèn)題是在同一時(shí)間上有大量的請(qǐng)求過(guò)來(lái),然后我們的系統(tǒng)要怎么抗住這些請(qǐng)求帶來(lái)的壓力件豌。比如在線直播服務(wù)疮方,同時(shí)有上百萬(wàn)甚至上千萬(wàn)人觀看。比如秒殺品茧彤,同時(shí)有大量用戶涌入骡显。
高并發(fā)是從業(yè)務(wù)角度去描述系統(tǒng)的能力,實(shí)現(xiàn)高并發(fā)的手段可以采用分布式曾掂,也可以采用緩存等惫谤,當(dāng)然也包括多線程、協(xié)程珠洗,但遠(yuǎn)遠(yuǎn)不僅如此石挂;高并發(fā)的基本表現(xiàn)為單位時(shí)間內(nèi)系統(tǒng)能夠同時(shí)處理的請(qǐng)求數(shù),高并發(fā)的核心是對(duì)資源的有效壓榨险污,有限的資源應(yīng)對(duì)大量的請(qǐng)求。
現(xiàn)代互聯(lián)網(wǎng)服務(wù)富岳,基本上都要考慮高并發(fā)問(wèn)題蛔糯,因?yàn)橐话愕漠a(chǎn)品,用戶的請(qǐng)求量都很大窖式。
二蚁飒、高并發(fā)架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)
高并發(fā)架構(gòu)設(shè)計(jì),需要從三大層來(lái)建設(shè)和分析
基礎(chǔ)設(shè)施層:這個(gè)是最基礎(chǔ)的依賴萝喘,主要是一些服務(wù)的部署淮逻。針對(duì)大公司而言,一般都有成熟的系統(tǒng)和基礎(chǔ)設(shè)施來(lái)承載阁簸,可能針對(duì)業(yè)務(wù)開(kāi)發(fā)的同學(xué)來(lái)看爬早,平時(shí)關(guān)注的的細(xì)節(jié)并不深入,但是不妨礙我們從全局角度來(lái)剖析高并發(fā)的設(shè)計(jì)启妹。
服務(wù)端架構(gòu)層:這個(gè)是我們重點(diǎn)要關(guān)注的架構(gòu)設(shè)計(jì)筛严,架構(gòu)設(shè)計(jì)不合理,就很難抗住高并發(fā)饶米,主要包括各種架構(gòu)和模塊的設(shè)計(jì)桨啃。
服務(wù)應(yīng)用層: 這個(gè)主要是針對(duì)我們寫的代碼來(lái)進(jìn)行優(yōu)化改進(jìn)。從代碼架構(gòu)檬输、代碼性能等方便去抗并發(fā)照瘾。
2-1、基礎(chǔ)設(shè)施層
部署:多 IDC + 異地多活
基礎(chǔ)設(shè)施層一般包含了服務(wù)器丧慈、IDC析命、部署方式等等。目前而言,我們一般都用容器部署的方式碳却,而容器的底層能力基本也都是建立在 k8s 容器層之上的队秩,服務(wù)本身的部署管理已經(jīng)有成熟的設(shè)施幫我們實(shí)現(xiàn)了。具體到部署方式昼浦,包括但不限于:
- 多 IDC 部署馍资。比如服務(wù)同時(shí)在廣州、上海兩地部署关噪。 這個(gè)依賴我們的服務(wù)是無(wú)狀態(tài)的
- 其他的參考下 異地多活架構(gòu)等相關(guān)部署鸟蟹。
監(jiān)控:可觀測(cè)性
系統(tǒng)的可觀測(cè)性主要包含三個(gè)部分: logging、tracing使兔、metrics建钥。 這個(gè)一般都要引入可觀測(cè)系統(tǒng),這樣才能幫助我們?cè)诋惓5臅r(shí)候能夠快速定位問(wèn)題虐沥。屬于必備設(shè)施熊经,一般而言,公司應(yīng)該都有專門的團(tuán)隊(duì)去做這個(gè)事情欲险。
2-2镐依、服務(wù)端架構(gòu)層
服務(wù)端架構(gòu)層是我們重點(diǎn)要關(guān)注的,這個(gè)也是考量個(gè)人架構(gòu)能力的最關(guān)鍵部分天试。
系統(tǒng)分層設(shè)計(jì):分層槐壳、分割、分布式
-
架構(gòu)分層
- 將系統(tǒng)在橫向維度上切分成幾個(gè)部分喜每,每一層的功能職責(zé)要足夠單一务唐,然后通過(guò)上層對(duì)下層的依賴和調(diào)度組成一個(gè)完整的系統(tǒng)
- 比如把電商系統(tǒng)分成:應(yīng)用層,服務(wù)層带兜,數(shù)據(jù)層枫笛。(具體分多少個(gè)層次根據(jù)自己的業(yè)務(wù)場(chǎng)景)
- 應(yīng)用層:網(wǎng)站首頁(yè),用戶中心刚照,商品中心崇堰,購(gòu)物車,紅包業(yè)務(wù)涩咖,活動(dòng)中心等海诲,負(fù)責(zé)具體業(yè)務(wù)和視圖展示
- 服務(wù)層:訂單服務(wù),用戶管理服務(wù)檩互,紅包服務(wù)特幔,商品服務(wù)等,為應(yīng)用層提供服務(wù)支持
- 數(shù)據(jù)層:關(guān)系數(shù)據(jù)庫(kù)闸昨,nosql數(shù)據(jù)庫(kù) 等蚯斯,提供數(shù)據(jù)存儲(chǔ)查詢服務(wù)
-
業(yè)務(wù)分割
- 在縱向方面對(duì)業(yè)務(wù)進(jìn)行切分薄风,將一塊相對(duì)復(fù)雜的業(yè)務(wù)分割成不同的模塊單元,對(duì)應(yīng)的是模塊的劃分拍嵌,通過(guò)合理的模塊劃分遭赂,使得每個(gè)模塊都能可以滿足 高內(nèi)聚低耦合 的設(shè)計(jì)要求,這樣不同的模塊可以分布式部署横辆,也能提高并發(fā)處理能力和功能擴(kuò)展
- 比如用戶中心可以分割成:賬戶信息模塊撇他,訂單列表模塊,充值模塊狈蚤,優(yōu)惠券模塊等
-
分布式
- 分布式應(yīng)用和服務(wù),將分層或者分割后的業(yè)務(wù)分布式部署困肩,獨(dú)立的應(yīng)用服務(wù)器,數(shù)據(jù)庫(kù)脆侮,緩存服務(wù)器锌畸,當(dāng)業(yè)務(wù)達(dá)到一定用戶量的時(shí)候,再進(jìn)行服務(wù)器均衡負(fù)載靖避,數(shù)據(jù)庫(kù)潭枣,緩存主從集群
集群架構(gòu)設(shè)計(jì):應(yīng)用集群、數(shù)據(jù)集群
應(yīng)對(duì)高并發(fā)系統(tǒng)幻捏,不管是應(yīng)用層面還是數(shù)據(jù)層面卸耘,單機(jī)都不可能搞定,因此都需要搭建集群架構(gòu)粘咖,然后通過(guò)負(fù)載均衡來(lái)對(duì)外提供服務(wù)。同時(shí)集群架構(gòu)還能保證系統(tǒng)的可用性侈百,當(dāng)某臺(tái)服務(wù)或者機(jī)器異常瓮下,負(fù)載均衡會(huì)自動(dòng)剔除,不會(huì)影響對(duì)外服務(wù)钝域。
-
應(yīng)用服務(wù)器集群
- nginx 反向代理
- slb
- LVS …
-
數(shù)據(jù)集群(關(guān)系/nosql數(shù)據(jù)庫(kù))
- 主從分離讽坏,一主多從
- 數(shù)據(jù)讀寫分離
數(shù)據(jù)庫(kù)設(shè)計(jì):讀寫分離+分庫(kù)分表+冷熱分離
應(yīng)對(duì)高并發(fā),數(shù)據(jù)的存儲(chǔ)例证,首先就要做好預(yù)估路呜,先進(jìn)行分庫(kù)分表 和 讀寫分離,最后可以根據(jù)情況來(lái)看是否冷熱分離:
讀寫分離织咧≌痛校互聯(lián)網(wǎng)系統(tǒng)大多數(shù)都是讀多寫少,因此讀寫分離可以幫助主庫(kù)抗量笙蒙。一般我們都是一主多從的架構(gòu)抵屿,可以抗量,也可以保證數(shù)據(jù)不丟捅位。分庫(kù)分表只能解決 QPS 高轧葛,但是無(wú)法解決 TPS 高搂抒,比如寫入的量足夠大的話(TPS 高),就得讀寫分離尿扯。
-
分庫(kù)分表求晶。數(shù)據(jù)存儲(chǔ)量大的時(shí)候,就需要通過(guò)分庫(kù)分表來(lái)存儲(chǔ)衷笋。先分芳杏,避免后期要拆,后期拆的話右莱,就面臨洗數(shù)據(jù)的問(wèn)題蚜锨,就需要采用雙寫模式來(lái)搞定。
- 分庫(kù)分表模式雖然能顯著提升數(shù)據(jù)庫(kù)的容量慢蜓,但會(huì)增加系統(tǒng)復(fù)雜性亚再,而且由于只能支持少數(shù)的幾個(gè)維度讀寫,從某種意義上來(lái)說(shuō)對(duì)業(yè)務(wù)系統(tǒng)也是一種限制晨抡,因此在設(shè)計(jì)分庫(kù)分表方案的時(shí)候需要結(jié)合具體業(yè)務(wù)場(chǎng)景氛悬,更全面的考慮。
冷熱分離耘柱。針對(duì)業(yè)務(wù)場(chǎng)景而言如捅,如果數(shù)據(jù)有冷熱之分的話,可以將歷史冷數(shù)據(jù)與當(dāng)前熱數(shù)據(jù)分開(kāi)存儲(chǔ)调煎,這樣可以減輕當(dāng)前熱數(shù)據(jù)的存儲(chǔ)量镜遣,可以提高性能。
不過(guò)士袄,既然是高并發(fā)系統(tǒng)悲关,不能應(yīng)用層直接讀寫 DB 的,一定有一個(gè)緩存在上面娄柳,如果直接讀寫 DB 能夠搞定寓辱,其實(shí)不能叫高并發(fā)了,只能說(shuō)是并發(fā)有點(diǎn)高赤拒。在非互聯(lián)網(wǎng)系統(tǒng)里面還是可以的秫筏。
緩存設(shè)計(jì):多級(jí)緩存架構(gòu)和本地緩存
緩存的最大作用是可以提升系統(tǒng)性能,保護(hù)后端存儲(chǔ)不被大流量打垮挎挖,增加系統(tǒng)的伸縮性这敬。緩存的設(shè)計(jì),需要分多個(gè)思路并行
-
首先要考慮的蕉朵,就是必須在數(shù)據(jù)庫(kù)之上鹅颊,增加一層分布式緩存,比如 Redis 或者 Memcached墓造。
- 這里需要考慮一下緩存和數(shù)據(jù)庫(kù)一致性的問(wèn)題堪伍。
-
其次需要考慮的是多級(jí)緩存架構(gòu)锚烦。分幾級(jí)緩存設(shè)計(jì),同時(shí)設(shè)計(jì)熱點(diǎn)緩存架構(gòu)帝雇。
- 在分布式緩存之上涮俄,還可以加一個(gè)本地緩存,來(lái)緩存最熱的數(shù)據(jù)
- 采用多個(gè)分布式緩存來(lái)搭建多級(jí)緩存尸闸。
消息隊(duì)列設(shè)計(jì):MQ 抗量和削峰
針對(duì)流量突峰彻亲,僅僅有緩存來(lái)抗量可能還不夠,還需要使用消息隊(duì)列來(lái)削峰吮廉。使用消息隊(duì)列后苞尝,可以將同步處理的請(qǐng)求改為 通過(guò)消費(fèi) MQ 消息來(lái)異步消費(fèi),這樣可以大大減少系統(tǒng)處理的壓力宦芦,增加系統(tǒng)的并發(fā)量宙址。常用的消息隊(duì)列比如 kafka。
服務(wù)治理設(shè)計(jì):超時(shí)调卑、熔斷抡砂、降級(jí)、限流等
超時(shí)恬涧、熔斷注益、降級(jí)、限流等都是常規(guī)策略溯捆,可以在另外服務(wù)治理章節(jié)去細(xì)看丑搔。
資源隔離設(shè)計(jì): SET 部署
資源隔離有各種類型,物理層面的服務(wù)器資源提揍、中間件資源啤月,代碼層面的線程池、連接池碳锈,這些都可以做隔離。
一般我們最常見(jiàn)的就是應(yīng)用部署層面的欺抗,比如 SET 化部署售碳。一個(gè)服務(wù)對(duì)外的使用方可能有 A 業(yè)務(wù)、B 業(yè)務(wù)绞呈,那么如何保證 AB 業(yè)務(wù)不會(huì)相互影響贸人,那么就是 SET 化部署。 SET 化部署也可以防止非關(guān)鍵業(yè)務(wù)來(lái)影響關(guān)鍵核心業(yè)務(wù)佃声。一個(gè)隔離的維度可以是按業(yè)務(wù)場(chǎng)景區(qū)分艺智,分為關(guān)鍵集群、次關(guān)鍵集群和非關(guān)鍵集群三類圾亏,這樣能避免關(guān)鍵和非關(guān)鍵業(yè)務(wù)互相影響十拣。
SET 化部署就是把業(yè)務(wù)系統(tǒng)分為多個(gè)可擴(kuò)展的邏輯分區(qū)封拧,每個(gè) SET 化的邏輯分區(qū)都可以獨(dú)立部署并提供服務(wù),SET 也可以理解為 ”邏輯機(jī)房“ 夭问,主要目的就是為了進(jìn)行獨(dú)立部署并且做到業(yè)務(wù)上的邏輯隔離泽西。
關(guān)于 SET 的具體例子:微信紅包用戶發(fā)一個(gè)紅包時(shí),微信紅包系統(tǒng)生成一個(gè)ID作為這個(gè)紅包的唯一標(biāo)識(shí)缰趋。接下來(lái)這個(gè)紅包的所有發(fā)紅包捧杉、搶紅包、拆紅包秘血、查詢紅包詳情等操作味抖,都根據(jù)這個(gè)ID關(guān)聯(lián)。紅包系統(tǒng)根據(jù)這個(gè)紅包ID灰粮,按一定的規(guī)則(如按ID尾號(hào)取模等)仔涩,垂直上下切分。切分后谋竖,一個(gè)垂直鏈條上的邏輯Server服務(wù)器红柱、DB統(tǒng)稱為一個(gè)SET。各個(gè)SET之間相互獨(dú)立蓖乘,互相解耦锤悄。并且同一個(gè)紅包ID的所有請(qǐng)求,包括發(fā)紅包嘉抒、搶紅包零聚、拆紅包、查詳情詳情等些侍,垂直stick到同一個(gè)SET內(nèi)處理隶症,高度內(nèi)聚。通過(guò)這樣的方式岗宣,系統(tǒng)將所有紅包請(qǐng)求這個(gè)巨大的洪流分散為多股小流蚂会,互不影響,分而治之耗式,
2-3胁住、服務(wù)應(yīng)用層
多線程、線程同步刊咳、協(xié)程
并發(fā)問(wèn)題一直是服務(wù)端編程中的重點(diǎn)和難點(diǎn)問(wèn)題彪见,為了優(yōu)化系統(tǒng)的并發(fā)量,單機(jī)解決高并發(fā)問(wèn)題從最初的 Fork 進(jìn)程開(kāi)始娱挨,到進(jìn)程池/線程池余指,再到 Epoll 事件驅(qū)動(dòng)(Nginx),再到協(xié)程(如 Goroutine)跷坝。
對(duì)于 Go 語(yǔ)言酵镜,盡可能的多使用協(xié)程去提高并發(fā)能力碉碉。
異步化
消息隊(duì)列也是一種異步化操作,但是除了依賴外部的中間件如消息隊(duì)列笋婿,在應(yīng)用內(nèi)我們也可以通過(guò)線程池誉裆、協(xié)程的方式做異步化,能異步的盡量異步處理缸濒,這樣可以提高并發(fā)足丢。
預(yù)處理:預(yù)加載、預(yù)熱
系統(tǒng)的預(yù)熱一般有JVM預(yù)熱庇配、緩存預(yù)熱斩跌、DB預(yù)熱等,通過(guò)預(yù)熱的方式讓系統(tǒng)先“熱”起來(lái)捞慌,為高并發(fā)流量的到來(lái)做好準(zhǔn)備耀鸦。 預(yù)熱實(shí)際應(yīng)用的場(chǎng)景有很多,比如在電商的大促到來(lái)前啸澡,我們可以把一些熱點(diǎn)的商品提前加載到緩存中袖订,防止大流量沖擊DB。
還有一種預(yù)熱的思路是利用業(yè)務(wù)的特性做一些預(yù)加載嗅虏,比如 feeds 流刷新的時(shí)候洛姑,提前加載 1-2 頁(yè)數(shù)據(jù),這樣用戶往下刷新的時(shí)候皮服,就感覺(jué)不到卡頓恩伺。