畢業就在小公司躺了3年,面試大廠發現,發現不會分散式沒人要…

敖丙發表於2020-08-12

點贊再看,養成習慣,微信搜一搜【三太子敖丙】關注這個文縐縐的程式設計師。

本文 GitHub https://github.com/JavaFamily 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

前言

之前寫了一篇秒殺系統的文章,最後給自己埋了分散式事務的坑,然後很多讀者就要求我去寫分散式事務,那作為程式設計師屆的暖男,我一向是有求必應的,就算是不睡覺我都要寫給你們看的!

因為分散式事務是:分散式 + 事務 = 分散式事務。

理所當然的要先談談分散式,而分散式又得談談這個概念是如何演進得來的,因此作為創作鬼才的我,就先來講講架構的演進,什麼叫分散式?什麼是叢集?SOA、微服務這兩個東西的關係和區別,下篇再講分散式事務。

因為我發現我讀者大多都是學生或者跟我一樣剛畢業不久,那一直聽分散式估計都聽膩了,估計都還不知道分散式的一些細節和架構演進路線吧。

先來說個題外話,我一直追崇一個技術點不僅要理解其原理,還需要知道這個技術點解決了哪些痛點,這些痛點又是由什麼引發的?這個技術還未誕生的時候是如何解決的?

也就是整體的演進過程,歷史脈絡。

比如為何需要HTTP?HTTP0.9為何需要演進到HTTP1.0? 進而又向1.1、2演進到最新要將Google開發的基於UDP的QUIC協議應用到HTTP3中。

搞懂這些來龍去脈相信你不僅僅對HTTP會有更深層次的理解,對網路也會有更加深刻的認識。當然也不是說一樣東西一來就得全盤理清,有些東西還是比較複雜的,只是說我們要往這個方向去學。

這也是我一直覺得很重要的一個學習思想,知其然知其所以然,會讓大家更好的理解很多東西。

一般架構的演進過程

好了,讓我們回到今天的主題,我們們先來看看一般架構的演進過程。

為什麼說一般呢?舉個例子,比如某線下龍頭企業,現在想開展網上相關業務,那麼有大批線下忠實使用者的支撐,並且自身錢包鼓鼓,人力財力都不缺。

那麼線上的軟體架構就得考慮清楚了,幾乎不可能按照初始階段來,當然也不能用力過猛,實際得靠架構師以及團隊實力進行權衡。

單體應用架構

什麼是單體應用?簡單的說就是不管啥功能都往一個應用裡寫,比如電商系統。使用者功能、商品功能、訂單功能等等,都往一個應用裡寫。

這有什麼好處?

在專案初期,小公司人力財力不足,急於拓寬市場,這種單體應用架構簡單粗暴,將所有的功能都打包在一個的應用中,直接部署。

本地開發除錯方便,直接起一個專案,除錯也是在一個程式內,沒有冗長跨程式的呼叫鏈,出錯可快速定位。

本地的函式呼叫,沒有網路呼叫的開銷

線上出了問題回滾這一個應用即可(這一點其實在某種程度上看是優點,某種程度上看是缺點)。

總結的說就是開發、測試、部署方便,本地呼叫對於遠端呼叫效能較好。

有什麼壞處?

系統耦合性高,導致開發效率低下

一開始可能模組結構還很清晰,隨著需求日益增長,不斷的新增新功能,程式碼量巨增,模組之間的邊界開始模糊,呼叫關係開始混亂,整體的程式碼質量非常依賴個人水平。

假使某個同事水平較差,實現的程式碼冗餘,邏輯混亂,這時候要在上面新增新功能或者修改老功能其實是一件很困難的事情,你不能保證你修改的功能模組不會影響到其他功能

而且程式碼會有**“破窗效應”**(這裡其實不僅僅是單體架構,對於所有架構來說都是如此,只是單體應用更大的龐大,業務界限不清晰,因此這種問題更容易被放大)。

有些人看到這就可能會說,這上面還說開發方便,這就又效率低下了?是的,過猶不及。

再比如一個新需求上線例如簡訊相關的,並且訂單也做了一些改造,但是簡訊功能出了 bug,需要回滾的是整個應用,訂單模組冤啊,陪著一起回滾。

我在老東家做電商活動,我們上線一個需求可能涉及6、7個服務,回滾也得全部回滾,而且都是負載均衡的,那機器可能就是上百臺了。

語言單一,不能根據場景選擇更加合適的語言,例如要實現資料分析,應用的語言是 Java,那麼就不能利用到 Python 豐富的類庫。

系統的整體可靠性不高,什麼意思呢?還是拿簡訊功能說事,新上的簡訊功能寫的有 bug,不管是堆疊溢位還是死迴圈等等,核心的訂單等功能都會受到影響。 因此你上線的功能有問題影響的不僅僅是這個功能模組,可能是整體系統的癱瘓。

系統不易於擴充套件部署,假設你發現你們的商品查詢的流量特別大,頂不住就得加機器。因為是單體應用所以為了商品查詢這一個功能,你需要在新加的機器上部署這一個應用,沒法單獨為這一個功能做定製化部署,對硬體資源有一定的浪費。

總結的說缺點就是隨著需求不斷增長,程式碼結構日益複雜,各功能摻雜在一起,系統耦合性高,模組之間邊界維護非常依賴開發者的個人水平。

模組之間經常會有公共功能難以劃分清楚,新增或修改功能困難,不確定是否會影響到其他模組,所有功能都在一個程式內,某個功能出問題可能影響的就是整個應用,而且無法根據特點場景選擇更加合適的語言去實現功能,技術單一。

隨著使用者的增長,無法做到熱點功能單獨擴充套件,只能整體應用部署。

至此我們已經明白了單體應用架構的優缺點,可以看出初始階段單體應用優點突出,隨著需求和使用者的增長漸漸的單體應用頂不住,缺點在不斷的放大。

也就是說你的產品需要發展到一定的階段,單體應用才會頂不住。在這之前單體應用是你的最佳選擇。

你要是說我的產品肯定頂的,所以一開始就大刀闊斧的設計,單體應用太 low 堅決不用。

可以的,秀出你的花樣,你有你的 Young。

我們再來看下單體應用一般的架構圖,注意單體應用不是真的就線上部署一個,好歹得兩臺,互相 backup 下,不能太虎一臺頂。

又過了一段時間,你發現你們還需要開發手機版。於是你們的架構又變成下圖所示的樣子。

沒錯,為了讓每個端不會相互影響,粗暴的拷貝現有的應用,稍加修改即可為手機版和小程式提供服務,你會發現很多功能程式碼都是重複的。這時候來個需求你改的就是多份程式碼了。

微服務架構

又過了段時間你已經強烈感受到單體應用所帶來的痛點,這證明你的產品發展的不錯,一開始肯定會忍,繼續忍,終有一天你會一拍桌子!來開個會我們們得還債了。

理所當然的你會根據不同業務拆分出不同的服務,並且會整理出當前公共的功能變成一個公共服務,每個服務獨立部署,獨立執行,程式碼進行了物理隔離,一個小團隊維護一個服務或多個服務。

並且一般而言服務化了,資料庫也會拆分出來每個服務維護自己的資料庫,資料庫之間的資料通過介面傳遞,而不再是直接訪問。

此時你的系統變成了下圖所示的樣子。

那現在解決了單體應用什麼問題?

系統的耦合度降低,模組之間的邊界清晰,都按業務物理隔離了。

在一定的措施下(下文會提到),系統整體可靠性變高

技術選型豐富,不同的服務可以利用不同的技術或語言實現,例如資料分析服務可以用 Python 實現,一些底層的服務團隊說我要用 GO,那就用 GO 唄。

可根據服務擴充套件部署,商品服務訪問量特大,那我們就單單給商品服務擴容,增加機器,其他服務照舊。

這就是微服務了。

好像微服務架構解決了單體應用的所有痛點啊?別急上面只是微服務的一部分,真正的微服務架構還需要包含很多東西,微服務是解決的單體應用的痛點,但是也引入了新的痛點!

服務化了之後上線某個需求,如果是單個服務內的不影響其它服務的你會感到很舒服,如果這個改動是介面層面的改動,涉及到多個服務,你就會覺得有點難受了。

上線之前需要定製好服務上線的順序,定製好每個服務的回滾計劃,涉及到每個團隊之間的合作,上線不再是一個簡單的打包、部署的過程。

出了問題也不是一個簡單回滾的過程,而可能是各個團隊分別回滾各自的服務。如果你自己的服務出了問題你會很焦慮,別的團隊都等著。如果別的團隊出了問題你也焦慮,怎麼還沒好啊。

你還會發現本地開發如果依賴別的服務會異常的難受,特別是你依賴的服務還依賴別人的服務,除錯、測試將變的複雜。

而且你會發現呼叫鏈路變長,呼叫增加了網路的開銷,效能變差。而且出錯難以定位問題來源。因此你需要引入分散式鏈路追蹤服務來定位問題

還需要引入ELK來方便日誌的檢視,分析問題

為了能夠動態擴容,你的服務需要自動註冊且能被自動發現,因此需要個註冊中心

網路之間的呼叫較為不可靠,因此還需要讓呼叫有重試機制,防止其他服務出 bug 或其他原因瘋狂呼叫你的服務,還需要有限流措施。為了防止一個服務掛了導致整體的雪崩需要有熔斷措施

為了在特殊時候例如大促的時候讓出硬體資源給核心功能,還需要有降級策略

上面說的重試、限流、熔斷、降級就是上文提到的一定措施下,可靠性變高。

而且每個服務都需要配置,因此還得有個配置中心,來做統一管理。

服務太多了,呼叫關係複雜為了對呼叫者更加的友好,並且還需要對呼叫進行許可權等控制,因此需要有個閘道器,對外暴露統一的介面,當然想限流什麼的可以在閘道器實現。

當然整體的監控是必不可少的,對所有的服務都需要做到全面的監控。

其他的還有啥DevOps、容器等等

可以看到服務化之後需要引入太多太多的東西,有人可能說你這也就才幾個服務啊,我上面的服務其實可以再細分。

例如商品的修改和寫入動作相比較於商品的瀏覽訪問量肯定少很多,那我就將商品的瀏覽再剝離出來單獨做一個服務,這樣便於擴容。

這使用者量上來,訪問量增加這樣的服務剝離你會發現越來越多,服務的數量到時候就上來了,而且需求也會不斷增加,推薦服務啊、搜尋服務啊等等很多很多,只是為了簡便都沒列出來。

上面提到的那些服務於微服務的元件也得部署,也得保證可靠性...你看這系統就越來越複雜了,所以服務化之後解耦了業務,卻又融入了非業務相關的東西

不過服務化其實是一個自然的結果,就像我們平時去的辦事大廳會根據服務類別劃分成不同的服務視窗,為了辦一件事情我能可能需要在各個視窗之間來回走動,這對應的不就是呼叫鏈路長嘛?走動的耗時等於我們呼叫的網路開銷。

所以說微服務架構是發展到一個階段自然而然的演進產物,早在微服務這個概念被提出之前,很多公司就已經是這樣乾的了。

我上面提到的其實是微服務1.0架構,而微服務2.0就是為了將非業務功能剝離出來而提出的,將服務治理的功能放在 SideCar 即邊車上,使得開發者專注於應用業務的開發,進而演進出 Service Mesh 即服務網格架構。

SOA和微服務

談到微服務你會發現 SOA 這個名詞經常伴隨著出現。

關於SOA和微服務我查閱了很多資料,不過對於這兩個名詞的解釋都各執一詞,沒有一個統一的答案。今天我就說說我的理解,拋磚引玉,有紕漏之處,敬請指正。

SOA,全稱 Service-Oriented Architecture即面向服務的架構。說到SOA就離不開 ESB,全稱Enterprise Service Bus。SOA和微服務一樣都是面向服務的。

可以看到 SOA 架構通過企業服務匯流排進行互動,也就是說中心化,需要按照匯流排的標準進行開發改造,而微服務是去中心化的。

其實我們可以抓到關鍵字企業,SOA 我認為是企業級別的面向服務概念,而微服務是應用級別的概念

兩種都是面向服務,只是 SOA 注重的是企業資源的重複利用,把企業的各個應用通過 ESB 進行整合。

而微服務注重的是應用級別的服務劃分,使得應用內服務邊界清晰,易擴充套件

這兩者其實是兩個方向的面向服務,互不衝突。還能是包容的結構,如下圖所示

分散式和叢集

分散式可以認為是通過網路連線多個元件而形成的系統。

廣義上說前後分離的應用就能算分散式,前端的 js 程式碼在瀏覽器跑著,後端的程式碼在伺服器跑著,兩種不同的元件合力對外提供服務構成分散式。

而我們常提到的分散式是狹義上的,指代不同的元件通過協作構成的系統

叢集常指的同一個元件多例項而構成邏輯上的整體

這兩個概念不衝突,分散式系統裡面可以包含叢集,像我們的商品服務就可以是叢集部署。

絮叨

今天主要簡述了下架構的演進,單體應用的優缺點以及微服務的優缺點。

再談了談SOA 和微服務之間的區別,以及分散式和叢集的區別。

說了這麼多,也不知道有沒有說清楚,個人能力有限,如果有紕漏,敬請指正,分散式事務也在瘋狂爆肝中,我們下篇文章見。

另外,敖丙把自己的面試文章整理成了一本電子書,共 1630頁!目錄如下,還有我複習時總結的面試題以及簡歷模板

現在免費送給大家,在我的公眾號三太子敖丙回覆 【888】 即可獲取。

我是敖丙,你知道的越多,你不知道的越多,我們下期見!

人才們的 【三連】 就是敖丙創作的最大動力,如果本篇部落格有任何錯誤和建議,歡迎人才們留言!


文章持續更新,可以微信搜一搜「 三太子敖丙 」第一時間閱讀,回覆【資料】有我準備的一線大廠面試資料和簡歷模板,本文 GitHub https://github.com/JavaFamily 已經收錄,有大廠面試完整考點,歡迎Star。

相關文章