面試官:你給我畫一下秒殺系統(tǒng)的架構(gòu)圖!
??
本文轉(zhuǎn)載自微信公眾號「Java補習課」,作者九靈。轉(zhuǎn)載本文請聯(lián)系Java補習課公眾號。
淚目,不堪回首!
博主畢業(yè)4年了,最近秋招開始了,每次回想起自己的秋招,都感覺到當時自己特別的可惜(菜是原罪),自己當時簡歷上面的項目,只有一個 農(nóng)資電商平臺,當時的秒殺系統(tǒng)還沒有那么普及(簡歷人均秒殺系統(tǒng))。
第一次微眾面試
當年自己的八股文背的其實還可以,但是自己的項目就只是一個單機系統(tǒng),分布式?微服務(wù)?什么玩意?,還記得當時微眾面試,是二面,在一個酒店房間,面試官笑嘻嘻的看著我,說讓我先畫一下我項目里面的農(nóng)資電商平臺, 我腦子嗡嗡叫,啥?咋畫, 就一個安卓系統(tǒng),一個前端頁面,和一個后臺系統(tǒng)?
大概長這樣子
我擦,這也太簡單了吧, 我是不是該畫復(fù)雜一點? 或者說,我這個能叫架構(gòu)嗎?就這樣,猶豫之間,毛線都沒有畫出來... 我記得當時好像畫了個這樣子的玩意。。毫無意外的,嗝屁了~
這玩意有點四不像,不說了,丟臉~
第二次微眾面試
第二次微眾面試,畢業(yè)有快一年了,抱著試一下的心態(tài),找了個師姐內(nèi)推, 那時候我在干啥呢,在搞爬蟲。公司離微眾比較近,就在金蝶那邊,下班了溜過去,跟面試官吧啦了一會八股文,好家伙,沒一會就掏出了一張紙:
來畫一下你們現(xiàn)在這個爬蟲系統(tǒng)的架構(gòu)圖!
當時系統(tǒng)的部署架構(gòu)長這樣吧, 比上面的看起來還簡單一點。
但是,我就是畫不出手啊!!!心里想著太簡單了啊!!這玩意能叫架構(gòu)嗎?
攤牌了, 我不會畫!
現(xiàn)在想起來,真的太憋屈了,年輕啊!那如果現(xiàn)在來回頭看的話,能怎么畫呢?
單體系統(tǒng)的部署架構(gòu)圖
爬蟲系統(tǒng)的分層架構(gòu)圖
爬蟲系統(tǒng)的業(yè)務(wù)架構(gòu)
架構(gòu)圖
從上面的各個方向描述架構(gòu)來看,其實即使是單體系統(tǒng) 也能夠畫出不一般的架構(gòu)圖!(為啥當時我就不會呢!)
最近在看架構(gòu)相關(guān)的內(nèi)容(華仔的課),在4+1 視圖里面,從多方面描述了我們的系統(tǒng),可以參考下面的描述,
你的秒殺系統(tǒng),架構(gòu)是怎么樣的?
單體系統(tǒng)
不管你們簡歷吹的多牛逼,我猜你們的服務(wù),大部分都是長這個樣子的,猜對的話點個關(guān)注, 只有瀏覽器是分布式的。
那我該如何去描述我的單體系統(tǒng)呢?
架構(gòu)設(shè)計的三大原則:
- 簡單原則
- 合適原則
- 演進原則
每一條原則都符合我們大學做的秒殺系統(tǒng)啊!!
簡單原則:一個系統(tǒng)就可以滿足我們秒殺服務(wù)的所有動作,沒有太多的中間件依賴
合適原則:在我們的實踐項目中,單體系統(tǒng)是最適合不過的了。(主要是沒錢啊!拆分服務(wù),引入中間件,部署集群,都得錢啊!)
演進原則:這個比較好理解,沒有什么系統(tǒng)架構(gòu)是一出生就定下來的,是隨著時間,業(yè)務(wù)需求,不斷演變出來的。
總結(jié):
我們架構(gòu)的優(yōu)勢: 成本低,系統(tǒng)復(fù)雜度低,維護成本低,快速定位問題
劣勢:穩(wěn)定性差,并發(fā)量低,擴展性弱等
在梳理架構(gòu)時,每個方案都有他的優(yōu)勢和缺點,所以需要了解你目前方案的優(yōu)缺點。才能更好的向面試官展示你的系統(tǒng)!
服務(wù)拆分
好家伙,參加了個科創(chuàng)比賽,資金到位了,能買更多機器了,那不得將服務(wù)優(yōu)化一下,拆分個微服務(wù)系統(tǒng)出來!
在這個服務(wù)拆分的架構(gòu)中,我們做了哪些動作?
- 靜態(tài)資源隔離(CDN加速)
- 代理服務(wù)器(Nginx)
- 服務(wù)拆分,應(yīng)用獨立部署
- 服務(wù)rpc通信 (rpc框架 & 注冊中心)
1、前后端分離
在單體系統(tǒng)中,我們的靜態(tài)資源(Html,JS,CSS 和 IMG)可能都是通過我們服務(wù)端進行返回,存在的問題是:
前端代碼維護成本比較高(全棧開發(fā)成本也高)
前端代碼發(fā)布,需要整個系統(tǒng)進行發(fā)布
服務(wù)器帶寬,請求資源占用等
那么通過前后端分離所帶來的好處就很明顯了:
代碼獨立維護(低耦合),發(fā)布成本低(高效率)
前后端通過接口交互動態(tài)數(shù)據(jù)
CDN資源訪問加速,減少后端服務(wù)壓力(高性能)
2、反向代理
反向代理的作用比較明顯, 由于我們服務(wù)拆分成多個,那么我們和前端進行交互時,需要提供一個通用的入口。而這個入口,就是我們的反向代理服務(wù)器(Nginx)。例如:服務(wù)域名:https://www.jiuling.com ,根據(jù)restful規(guī)范,我們可以通過 https://www.jiuling.com/user/1.0/login 將請求轉(zhuǎn)發(fā)到 用戶服務(wù)的登錄接口中。
3.進程間通信
隨著服務(wù)的拆分,在部分功能的實現(xiàn)上,就會涉及到服務(wù)間相互調(diào)用的情況,例如:
在常見的實現(xiàn)方案上,我們會采用 注冊中心 和 RPC框架,來實現(xiàn)這一能力。而我們比較常用的實現(xiàn)方案就是 zookeeper & dubbo。
為什么要使用 RPC 框架?
當我們提到使用 RPC框架 的時候,是否有去思考過,為什么要使用 RPC框架? 每個服務(wù)提供 RESTful 接口,不是也能夠完成服務(wù)間通信嗎?
這里就需要進行對比 RPC 和 RESTful 的區(qū)別了:
- 數(shù)據(jù)報文小&傳輸效率快:RPC簡化了傳輸協(xié)議中一些必要的頭部信息,從而加快了傳輸效率。
- 開發(fā)成本低:例如 Dubbo框架,封裝好了服務(wù)間調(diào)用的邏輯(如:反射,建連和超時控制等),只需要開發(fā)相應(yīng)的接口和數(shù)據(jù)模型即可。
- 服務(wù)治理: 在分布式場景下,我們的服務(wù)提供者不止一臺,那么就涉及到 服務(wù)健康,負載均衡和服務(wù)流控等情況需要處理,而這部分能力在rpc & 注冊中心 的架構(gòu)下,都已經(jīng)滿足了。
說完優(yōu)點后,再來分析一下,RPC的缺點:
- 耦合性強:相較于 RESTful而言,RPC 框架在跨語言的場景下實現(xiàn)比較困難。并且版本依賴比較強。服務(wù)脫離了當前內(nèi)網(wǎng)環(huán)境后,無法正常提供服務(wù),遷移成本高。
- 內(nèi)網(wǎng)調(diào)用:RPC更適合內(nèi)網(wǎng)傳輸,在公網(wǎng)環(huán)境下,顯得沒那么安全。
分布式微服務(wù)
在上一個版本的服務(wù)拆分中, 我們根據(jù)不同的業(yè)務(wù)邊界,功能職責,劃分出了多個子系統(tǒng),而針對不同的系統(tǒng),他所承受的負載壓力是不一樣的,例如:訂單服務(wù)的每個請求處理耗時較長(其他服務(wù)壓力不大),為了挺升我們的下單量,我們可以只擴容訂單服務(wù)即可,這就是我們在服務(wù)拆分所帶來的收益,性能使用率提升!
從上面的圖我們可以看到,有些服務(wù)出現(xiàn)了不同的重影,每一個方塊,可以理解為一臺機器,在這個架構(gòu)中, 為了保證我們的下單成功率,以及下單量,我們主要將服務(wù)器集中在了訂單服務(wù)。
除此之前,再來看看我們的中間件集群部署:
- mysql 主從架構(gòu):讀寫分離,減輕主庫壓力,確保數(shù)據(jù)能正常寫入,保障訂單數(shù)據(jù)落庫.
- zookeeper 主從架構(gòu):保障注冊中心可用,避免導致全鏈路雪崩。
- redis 哨兵集群:避免redis宕機導致大流量直接打到數(shù)據(jù)庫中。
小結(jié)
到這里為止,一般我們自己開發(fā)的系統(tǒng),也就基本完成了整個秒殺系統(tǒng)的演進了。可能大伙一直有個疑問,為什么少了我們最熟悉的MQ呢?
在整個調(diào)用鏈路中,我都是以同步調(diào)用的方式去講述這一個秒殺系統(tǒng)的架構(gòu),因為這個已經(jīng)滿足我們當前的流量訴求了,在架構(gòu)設(shè)計的原則里面,提到的,合適原則,和演進原則。在當前滿足流量需求的情況下,我們需要先思考引入消息中間件,帶來的問題是什么?解決的問題又是什么?在權(quán)衡利弊后,才是我們決策是否要使用這個方案的時候。
高性能
在上述架構(gòu)演進的過程中,我們通過服務(wù)拆分,垂直擴容,分布式部署等方式,提升了我們架構(gòu)的性能和穩(wěn)定性,對于我們自研階段的架構(gòu)演進已經(jīng)是足夠滿足我們的流量訴求了,但如果我們想繼續(xù)優(yōu)化我們的系統(tǒng),提升服務(wù)性能,可以從以下幾個方面進行優(yōu)化:
- 資源預(yù)熱
- 緩存預(yù)熱
- 異步調(diào)用
1、資源預(yù)熱
在上面的服務(wù)拆分階段, 我們就提到了資源動靜分離, 這里的靜態(tài)資源包括:html,js,css,img 等。我們活動階段,可以通過后臺管理系統(tǒng),將商品服務(wù)中的活動的靜態(tài)資源預(yù)熱到CDN,加速資源的訪問。
資源預(yù)熱: 通過預(yù)先將資源加載到CDN
回源:CDN找不到資源后,會觸發(fā)源站(商品服務(wù))調(diào)用,進行查詢對應(yīng)資源,如果源站存在該資源,則會返回到CDN中進行緩存。
OSS: 實際存儲靜態(tài)資源的服務(wù)(可參考阿里云OSS)
上面有反復(fù)提到,引入一個技術(shù)的時候,需要同時考慮它所帶來的利和弊,那么 CDN的風險是什么呢?
- 成本 : 比較直接,就是得多花錢!
- 帶寬 :在大流量的訪問下, CDN 是否能支撐那么多的帶寬,每個服務(wù)器能支撐的流量是有限的,需要考慮CDN是否能支撐業(yè)務(wù)的訪問量。
- CDN命中率: 在CDN命中率低的情況下,比如活動圖片,每一個小時都會發(fā)生改變,那么每次圖片的替換,都會觸發(fā)回源操作,這時候的資源訪問效率反而有所下降。
2、緩存預(yù)熱
與上面的靜態(tài)資源加速相對比,動態(tài)數(shù)據(jù)則需要通過緩存進行性能上的優(yōu)化,老生常談,為什么redis 那么快?
單線程(redis的性能瓶頸并不在這,所以這個不算優(yōu)勢)
- 多路I/O復(fù)用模型
- 數(shù)據(jù)結(jié)構(gòu)簡單
- 基于內(nèi)存操作
引入 redis 帶來的風險主要有:
- reids 宕機:單機部署的情況下,會導致大量的服務(wù)調(diào)用超時,最終引起服務(wù)雪崩??赏ㄟ^Sentinel集群優(yōu)化。
- 緩存擊穿:大流量下,緩存MISS和緩存過期等情況,會導致請求穿透到數(shù)據(jù)庫,如果數(shù)據(jù)庫扛不住壓力,會造成服務(wù)雪崩。可以通過 布隆過濾器進行優(yōu)化。
- 數(shù)據(jù)一致性:緩存數(shù)據(jù)與DB 的數(shù)據(jù)一致性問題,需要通過更新策略進行保障。
3、異步調(diào)用
通過異步的方式,將減庫存成功的用戶,通過消息的方式,發(fā)送給訂單服務(wù),進行后續(xù)的下單操作。可以在短時間內(nèi),將所有的商品銷售出去。整體的流程如下圖所示:
MQ異步調(diào)用為什么能過提升我們服務(wù)的吞吐量呢?
主要原因在于,通過異步調(diào)用的方式,我們將消息投遞過去了,就完成了這一次的請求處理,那么性能的瓶頸,由訂單服務(wù),轉(zhuǎn)移到了秒殺服務(wù)這里。通過減少調(diào)用依賴,從而提升了整體服務(wù)的吞吐量。
MQ 帶來的常見問題:
- 數(shù)據(jù)一致性
- 重復(fù)消費:由于生產(chǎn)者重復(fù)投遞消息,或者消費緩慢導致重復(fù)推送消息。需要通過加鎖,消費冪等來保證消費正常。
- 消息堆積:生產(chǎn)能力遠大于消費能力情況下,會導致消息堆積。
- MQ可用性:MQ宕機的情況下,需要支持同步調(diào)用切換。
- 這里不做詳細介紹,后面會專門寫一篇MQ相關(guān)的文章。
高可用
能看到這里真不容易,感謝大家的支持。關(guān)于可用性這里,之前有寫過一篇 # 《高可用實戰(zhàn)》-B站蹦了,關(guān)我A站什么事?感興趣可以看一下。
高可用主要可以從:
- 動態(tài)擴容:根據(jù)服務(wù)壓力,針對不同服務(wù)進行動態(tài)擴容。
- 限流熔斷:可參考我之前的文章:# 《高可用實戰(zhàn)》-B站蹦了,關(guān)我A站什么事?
- 異地多活: 通過多機房部署,避免物理攻擊!
同城雙活
部署在同一個城市不同區(qū)的機房,用專用網(wǎng)絡(luò)連接。兩個機房距離一般就是幾十千米,網(wǎng)絡(luò)傳輸速度幾乎和同一個機房相同,降低了系統(tǒng)復(fù)雜度、成本。
這個模式無法解決極端的災(zāi)難情況,例如某個城市的地震、水災(zāi),此方式是用來解決一些常規(guī)故障的,例如機房的火災(zāi)、停電、空調(diào)故障。
異地多活
在上述模式中,沒辦法解決城市級別的服務(wù)容災(zāi),比如水災(zāi),地震等情。而通過異地多活的部署方案,則可以解決這種問題。
但是每個方案都是存在利和弊的,那么異地多活的弊端主要體現(xiàn)在網(wǎng)絡(luò)傳輸和數(shù)據(jù)一致性的問題上!
跨城異地主要問題就是網(wǎng)絡(luò)傳輸延遲,例如北京到廣州,正常情況下的RTT(Round-Trip Time 往返時延)是50毫秒,
當遇到網(wǎng)絡(luò)波動等情況,會升到500毫秒甚至1秒,而且會有丟包問題。
物理距離必然導致數(shù)據(jù)不一致,這就得從“數(shù)據(jù)”特性來解決,
如果是強一致性要求的數(shù)據(jù)(如存款余額),就無法做異地多活。
圖片地址:draw.io原圖