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