6 張圖帶你搞懂微服務

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖說 微服務 早已是一個老生常談的話題了,在 infoq 或者 thoughtworks 上可以找到很多案例,不過可惜的是其中相當比例的案例是失敗的案例,究其原因,除了 技術門檻 之外,主要是因爲很多人脫離了實際情況,只是爲了微服務而微服務。本文通過一個例子帶領大家從頭到尾體驗一下微服務的演化過程,不僅要做到知其然,更要做到知其所以然。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設我們正在開發一個在線購物項目,其主要功能包括商城、推薦、評論、用戶等,它是一個典型的 單體架構 :不同團隊的技術人員工作在同一個版本庫上,系統功能按模塊劃分,不同模塊之間通過本地函數調用,通常操作同一個數據庫。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7b/7b26a12437b9572018221cefabfa59d5.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在項目早期,單體架構往往能很好的適應快速迭代的需求,不過隨着項目的發展,項目本身會變得複雜,其弊端不可避免的出現,比如下面列舉的一些情況:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲大家都工作在同一個版本庫上,所以可能會遇到:商城模塊完成了新功能,準備上線,結果推薦模塊剛提交了還沒來得及測試的代碼,於是不得不推遲上線。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不同的需求採用不同的技術棧:負責評論模塊的同事想用 PHP + MySQL 來構建系統,負責用戶模塊的同事卻想用 Golang + PostgreSQL 來構建系統。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有的模塊需要高性能 CPU,有的模塊需要大內存,因爲不同的模塊是耦合在一起的,所以我們的服務器不得不同時具備高性能 CPU,大內存,從而增加了成本。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何解決此類問題? 康威定律 給出了很好的建議:「設計系統的架構受制於產生這些設計的組織的溝通結構」,通俗點說就是:「有什麼樣的組織架構就會設計出什麼樣的系統架構」。在本例中,因爲不同的團隊負責不同的模塊,所以很自然的可以通過模塊來把系統切分成商城、推薦、評論、用戶等幾個獨立的服務:每個服務有自己獨立的版本庫和數據庫,服務之間通過 RPC 來通信。不同的服務擁有自己的版本庫,可以使用適合自己的技術棧和硬件,獨立開發獨立部署。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個需要注意的問題是如何確定服務粒度的大小,雖然按照康威定律的描述只要按照組織架構的大小來確定服務的大小即可,但是如何規劃一個合理的團隊規模呢?實際上並沒有一個精確的答案,我們需要按照客觀情況來確定一個適合自己的大小適中的服務粒度,過小的粒度會導致服務之間強耦合,過大的粒度則背離了微服務的初衷,Uber 甚至還針對服務粒度大小問題發明了一個 宏服務 的概念,有興趣的讀者不妨看看。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/12/127145f9cb43036492a8b5b6317e6057.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們把單體架構切分成獨立的服務之後,原本模塊間本地的函數調用變成了服務間遠程的 RPC 調用,我們不得不處理服務治理之類的問題,隨着微服務數量的增加,問題會變得越來越棘手,好在隨着雲原生的發展,特別是 K8S 和 istio 等技術的成熟,我們的架構可以演化到 service mesh 階段,通過 sidecar 透明實現服務治理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ae/aeee81ad6a6c96cbfb37e9b26c241e56.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果僅僅是把原本模塊間本地的函數調用變成了服務間遠程的 RPC 調用的話,那麼我們的微服務很可能會淪爲「 分佈式單體 」。問題的癥結在於過度使用 RPC,導致服務與服務之間強耦合,解決方法是引入 Event,通過 Event 實現服務與服務的解耦。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看看如何實現下面的業務邏輯:當一個用戶註冊後,要在商城裏給用戶一張優惠券。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 RPC(強調做什麼):當用戶模塊創建了一個新用戶的時候,通過 RPC 調用商城模塊給用戶一張優惠券,過程中用戶模塊和商城模塊是強耦合的。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 Event(強調發生了什麼):當用戶模塊創建了一個新用戶的時候,它發出一個 UserCreated 事件,商城模塊觀察到對應的事件後,給用戶一張優惠券,過程中用戶模塊和商城模塊是弱耦合的。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實際情況中應該按需求來選擇使用 RPC 或者 Event:如果是業務邏輯的實現部分,傾向於選擇使用 RPC;如果是業務邏輯完成之後的後續通知部分,強烈建議選擇使用 Event。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/16/16e84b6f1ec3caa5af40de5959bdc5b3.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務部署好了之後,接下來我們還需要考慮如何暴露服務以供前端調用,比如用戶瀏覽某個商品的詳情頁,內容包括商品數據、以及對應的推薦數據和評論數據,如果直接操作服務的話,那麼需要多次查詢商品服務、推薦服務、評論服務,並不可取,此時可以加入 ","attrs":{}},{"type":"link","attrs":{"href":"https://microservices.io/patterns/apigateway.html","title":null,"type":null},"content":[{"type":"text","text":"API Gateway","attrs":{}}],"marks":[{"type":"italic"},{"type":"strong"}]},{"type":"text","text":" 充當代理,前端只要請求 API Gateway 一次就可以拿到數據。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d0/d047577a498b05db9a0b0a727b60a6c6.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了 API Gateway 之後,它可以幫我們完成聚合之類的邏輯。不過有一個問題是前端可能有多種不同的類型,比如 PC 前端,Mobile 前端,它們的業務邏輯不可避免的會有各種各樣的差異,如果在 API Gateway 中處理這些差異的話,那麼會出現壞味道,爲了解決此類問題,我們引入 ","attrs":{}},{"type":"link","attrs":{"href":"https://microservices.io/patterns/apigateway.html","title":null,"type":null},"content":[{"type":"text","text":"BFF","attrs":{}}],"marks":[{"type":"italic"},{"type":"strong"}]},{"type":"text","text":" (Backend For Frontend),每一種前端都有屬於自己的 BFF,用來處理專屬於自己的業務邏輯,至於 API Gateway,則只處理鑑權,日誌等公共業務邏輯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/94418f6e272c9904f0d65dfe9a2cc2e2.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務是個極其複雜的概念,本文僅就一些表面問題淺談一二,其他諸如 SAGA 之類的複雜問題,由於篇幅所限,並未涉獵,大家如果有興趣的話請自行查閱。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後把 Martin Fowler 在 PoEAA 中提出的 分佈式對象第一定律 送給大家:不要分佈你的對象!套用這個說法的話,不難引申出微服務第一定律:不要使用微服務!雖然話裏有一些戲虐的成份,但是它至少告誡我們在面對微服務的時候要懷揣着一顆敬畏的心。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章