掃盲帖:聊聊微服務與分佈式系統

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"今天和大家聊一聊分佈式系統的相關概念及其常見分佈式組件和設計思想(不涉及計算機科學中分佈式系統的技術理論之類的東西),之前爲了準備這次的面試我是把市面上的很多分佈式組件都看了一遍,我們公司所用的分佈式組件基本也沒出我瞭解的那個知識圈(公司用了Apollo我沒提前瞭解,大E了)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5d/5d42bc89f8b973ec69c4da411960a729.jpeg","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"單體應用與集羣","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單體應用、集羣和微服務,這個標題一出你們可能就知道我想說啥了,emm,就是架構的演進過程,很多人可能都看過相關知識,不過我爲了文章的完整性還是打算簡單講一講。","attrs":{}}]},{"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":"text","marks":[{"type":"strong","attrs":{}}],"text":"單體應用","attrs":{}},{"type":"text","text":",在一個業務的起步階段,往往是用戶量不大且訪問請求少的階段,這個時候我們一般只需要部署一個Web應用和一臺數據庫就能滿足我們的業務需求,且隨着SpringBoot的流行,Web服務器可以內置在應用裏面,能夠更加方便快捷的部署應用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7c/7c169b6167f043b8334f012b196323ea.png","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"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":"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假如我們現在的單體Tomcat應用只能支撐QPS200,隨着用戶量的增大併發隨之增大,慢慢超出了單臺應用能承受的極限,假如現在達到了QPS300,那麼多出來的100請求就只能等着前面的請求處理完了之後才能請求進去,這樣帶來的後果就是響應時間變長,影響用戶體驗。","attrs":{}}]},{"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":"或者我們應用中的業務比較複雜,單次請求響應時間比較長,這樣的話大量請求擠壓也會導致用戶的體驗很差,他們能明顯的感覺到點擊某個按鈕之後隔了1~2s纔有結果返回。","attrs":{}}]},{"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":"text","marks":[{"type":"strong","attrs":{}}],"text":"集羣架構","attrs":{}},{"type":"text","text":"了,我們可以將單體應用同時部署兩份,並通過Nginx進行反向代理和負載均衡進行流量分流,這樣每個單體應用承受的QPS就是150了,就在可以接受的範圍內,而且維護集羣應用和維護單機應用區別不大,只是在涉及到鎖的時候可能要藉助分佈式鎖來做。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2f/2fc1ed7f97756fc5b7323868393ebc24.png","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"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":"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":"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":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"首先既然項目已經是集羣了證明業務量也不會非常小,這也就代表了代碼一定有一些規模了,這時候要面臨的第一個問題就是所有開發人員都會在這一個項目裏面修改代碼,極大情況下是衝突不斷。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"其次這麼多代碼聚合在一塊,當你進行一處功能修改的時候,如果需要進行全量回歸測試的話那簡直太要命了。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"再者說,所有業務塊都在一個系統這會導致資源無法最大化利用,比如一個電商系統肯定是商品搜索/推薦系統爲最常用的系統,理所當然在資源方面他們應該佔有更多的機器資源,但是業務不進行拆分想進行橫向擴展只能將所有業務一起擴展。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"最後是有可能會引起雪崩效應,舉個例子你的系統中假如有一塊非常不重要的業務(比如簽到)代碼寫的有問題在生產環境中引發了OOM,那麼它必然會連累到整個系統中的所有業務都變成不可用,因爲不同業務之間並沒有物理隔離。","attrs":{}}]}],"attrs":{}}]},{"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"微服務與分佈式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務是一種面向服務的軟件架構模式,自2014年","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Martin Fowler","attrs":{}},{"type":"text","text":"與","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"James Lewis","attrs":{}},{"type":"text","text":"寫了一篇微服務架構的文章之後(微服務這個概念在此之前就有),微服務就被大量的討論以及實踐到生產項目中:Netflix、Amazon這些商業公司都有微服務的成功案例,商業公司最會考慮的一件事就是成本,他們不會因爲Martin Fowler是軟件工程的名人(巨佬)就對他提出的某個概念迅速披掛上馬,一定是經過了很多權衡之後,纔對他們的項目使用微服務的架構模式。","attrs":{}}]},{"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":"text","marks":[{"type":"strong","attrs":{}}],"text":"Martin Fowler","attrs":{}},{"type":"text","text":"原文的配圖和翻譯來說明:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c7/c7e6b9b01efb086aa008ec720a45df36.png","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡而言之,微服務架構風格,就像是把一個單獨的應用程序開發爲一套小服務,每個小服務運行在自己的進程中,並使用輕量級機制通信,通常是 HTTP API。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些服務圍繞業務能力來構建,並通過完全自動化部署機制來獨立部署。這些服務使用不同的編程語言書寫,以及不同數據存儲技術,並保持最低限度的集中式管理。","attrs":{}}]}],"attrs":{}},{"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":"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":"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":"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"微服務是指的一種面向服務的軟件架構方式,而分佈式則是一種爲了某個共同目標而協調多臺計算機節點進行工作的軟件系統。","attrs":{}}]},{"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"業務的增加我們通過業務拆分來解決其帶來的問題,這可以算是微服務的範疇。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"不同的服務部署在不同的機器上,但是對於用戶來說卻和訪問一個系統沒什麼區別,依靠網絡將多個計算機節點組成一個統一的整體,這可以算是分佈式系統的範疇。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"由於數據量的不斷增加我們可以通過增加機器節點來分攤數據量,這算是分佈式系統中分佈式存儲的範疇。比如我們的Redis數據一臺機器已經存不下了,就要考慮使用RedisCluster將數據分散到多臺機器上面,其他例如Kafaka、ES都是分佈式架構的中間件都可以進行分佈式存儲。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"如果應用中某些計算量比較大的任務使用一臺機器執行會耗時過長,這時我們可以拆分任務給多臺節點同時執行,這算是分佈式系統中分佈式計算的範疇。最近我在接手項目的時候就遇到了類似的問題,原任務單機執行時間過久,讓我改爲所有在線節點同時執行,共同分攤任務,並且要能動態根據在線節點數量進行任務拆分。","attrs":{}}]}],"attrs":{}}]},{"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/46/46212bd8c9116b59734dec6eb88f9c45.jpeg","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"微服務是指的一種面向服務的軟件架構方式,而分佈式則是一種爲了某個共同目標而協調多臺計算機節點進行工作的軟件系統。","attrs":{}}]},{"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":"text","marks":[{"type":"strong","attrs":{}}],"text":"我們要實現一個微服務架構的應用時,我們就在實現一個分佈式系統。","attrs":{}}]},{"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Tips:","attrs":{}},{"type":"text","text":" 分佈式計算/存儲也是一門計算機科學中的研究方向,所以它們其實還是可以挺深奧的一堆東西。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"分佈式與CAP","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看了上一節大家應該已經明白了微服務和分佈式的關係了吧,我這篇文章的重點其實是分佈式,因爲微服務只是一個面向服務的軟件架構概念,讓我理解起來它的主要作用就是提出了服務拆分、獨立開發部署這些概念,emm~概念,但是一個分佈式系統卻有很多各種各樣的實際問題需要解決,所以我的重點是在分佈式。","attrs":{}}]},{"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":"先來說說分佈式的CAP原則,但凡對分佈式有點了解的,都不能不知道這個CAP,先來看看它的定義:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CAP原則又稱CAP定理,指的是在一個分佈式系統中,一致性(Consistency)、可用性(Availability)、分區容錯性(Partition tolerance)。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CAP 原則指的是,這三個要素最多隻能同時實現兩點,不可能三者兼顧。","attrs":{}}]}],"attrs":{}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"分區容錯性:","attrs":{}},{"type":"text","text":" 是指在分佈式系統中部署在不同地點的機器可能出現網絡連接失敗的情況,這就像我的推薦服務會請求商品服務裏面的數據,但有可能發生了網絡波動導致我推薦服務發起的請求網絡連接超時。","attrs":{}}]},{"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","marks":[{"type":"strong","attrs":{}}],"text":"可用性:","attrs":{}},{"type":"text","text":" 是指用戶的請求必須返回結果,要做到這個程度就需要我們在不同機房部署應用避免出現某些地方機房遭遇了事故導致服務宕機的情況。","attrs":{}}]},{"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","marks":[{"type":"strong","attrs":{}}],"text":"一致性:","attrs":{}},{"type":"text","text":" 是指數據被修改後,之後讀到的數據一定是最新的數據。","attrs":{}}]},{"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":"上面的定義已經說了,CAP只能最多同時實現兩點,因爲我們是分佈式系統,所以多臺服務節點是無法避免的,也就是分區容錯性我們必須要保證,所以我們只能保證實現CP或者AP。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b1/b1e62713817686d457871f4dfb017e5b.jpeg","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼不能同時實現CAP呢?原因很簡單,因爲可用性這個要求需要每個節點的數據都要有幾個冗餘的副本,用來保證有一個節點掛掉之後副本能頂上去。然而副本節點和主機節點之間因爲有網絡通信所以往往這個數據的傳輸是有延遲的,這也就不能保證主機的數據被修改後副本能立即收到修改,而是經過一頓延遲後才能收到主機修改的數據。","attrs":{}}]},{"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":"爲了方便大家理解,我再舉一個例子來說明一下,比如說分佈式中間件Redis:","attrs":{}}]},{"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":"它在單機的情況下可以保證CP,因爲只有一臺Redis節點所以數據被修改後之後的請求所訪問到的數據都是最新的。","attrs":{}}]},{"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":"它在集羣的情況下可以保證AP,AP是強調可用性,集羣架構下如果主節點掛掉之後,副本節點還可以接着響應請求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"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":"既然CAP無法同時保證,那我們就要退而求其次,這裏將會引出一個新的理論:BASE。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"BASE是Basically Available(基本可用)、Soft state(軟狀態)和Eventually consistent(最終一致性)的簡寫。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"BASE是對CAP中一致性和可用性權衡的結果,契合性思想是即使無法做到強一致性,但每個應用都可以根據自身的業務特點,採用適當的方式來使得系統達到最終一致性。","attrs":{}}]}],"attrs":{}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"基本可用:","attrs":{}},{"type":"text","text":" 是指某些情況下允許部分可用性的丟失,比如我們的雙十一大促,可能會由於下單量激增導致你下不了單,這就屬於下單服務不可用了,但是並不會持久太久,而是短暫的。","attrs":{}}]},{"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","marks":[{"type":"strong","attrs":{}}],"text":"軟狀態:","attrs":{}},{"type":"text","text":" 是指允許副本同步過程中出現延遲而導致副本不一致的情況。","attrs":{}}]},{"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","marks":[{"type":"strong","attrs":{}}],"text":"最終一致性:","attrs":{}},{"type":"text","text":" 是指系統的所有系統數據副本可以經過一段延遲後最終達到數據的一致性,不需要保證實時的數據一致。","attrs":{}}]},{"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":"我們既想要可用性也想要一致性,然而二者無法兼得,所以BASE理論就提出了這樣一種兼顧的思想,代價是強可用和強一致的損失。","attrs":{}}]},{"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":"現實中的很多系統中都是BASE理論這種思想,達到系統的一個基本可用和最終一致。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"分佈式開發與組件","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":"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面我已經說過,分佈式系統是多個節點組成一個整體,那不同節點的IP肯定不同,想要感覺像是一個整體,就得有一個部件在所有節點的最前面承擔一個請求分發的作用,這個東西就叫:網關。","attrs":{}}]},{"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":"一般的網關有兩層,第一層是服務器的Nginx+LVS這種,第二層是分佈式應用的網關,我這裏說的是分佈式應用的網關。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/47/47091b6327986d38d96aadc1dfd31ffb.png","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個網關的作用有點像Nginx,可以幫你做反向代理幫你做負載均衡,但是比它更強大,分佈式應用的所有請求第一站就是應用的網關層,在這裏可以校驗請求是否合法,阻擋網絡攻擊,也可以進行動態的請求分發。","attrs":{}}]},{"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":"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":"我們要想轉發首先要知道各個節點的IP和端口號,這個信息我們不能寫死,寫死的話不利於動態加機器,所以這個時候我們需要用到一箇中間價:註冊中心。","attrs":{}}]},{"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/99/996f6070b96807c63df3c23494cc7fbf.png","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉個例子:我有一個商品服務給它起了個名字叫做goods-service,我給他起了三臺服務器,這三臺服務器都會註冊到註冊中心上去,然後我們就可以在註冊中心上看到有一個叫goods-service的東西下面有三臺節點,每臺節點的IP端口號都會在註冊中心上面保存。","attrs":{}}]},{"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":"這時候我們網關來轉發的時候是通過goods-service這個名稱去註冊中心拿到三臺節點的地址,然後根據不同的策略最終決定我們是要將請求發到哪一臺節點。","attrs":{}}]},{"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":"請求發送到服某某服務的這個過程,我們可以稱作爲服務通信,這個通信方式一般有兩種:HTTP和RPC,這個其實我覺得沒啥好說的,主要就是通信的協議不一樣,協議的不同也造成了效率的不同。","attrs":{}}]},{"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":"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":"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":"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":"它對於某些公共的配置也很方便,比如六個微服務用的是一個Redis集羣,我們只需要配置一份就夠了,而且集羣配置有變化我們也只需要修改一次,六個微服務應用都能感知到。","attrs":{}}]},{"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":"text","marks":[{"type":"strong","attrs":{}}],"text":"哎呀,服務器太火爆了,請稍後再試呢”","attrs":{}},{"type":"text","text":" 這種提示,這個東西就是爲了保護淘寶服務不被沖垮而使用的一種限流措施,這種措施叫做服務降級。","attrs":{}}]},{"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":"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":"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"SpringCloud與Alibaba","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一節要給大家說說相關的實現,給大家認認臉熟,因爲我們搞Java應用開發的一般都是用Spring全家桶的,在微服務開發這塊Spring推出了它的SpringCloud全家桶用來做微服務分佈式開發。","attrs":{}}]},{"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":"經過這幾年的發展其實大致可以分爲兩套開發套件:SpringCloud開發套件和SpringCloudAlibaba開發套件,這個只是我個人的分法,還有一些其他家的我也會提一下。","attrs":{}}]},{"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":"我們先來看看SpringCloud這一套,這一套相關的組件一開始基本都是Netflix公司開發的,這是一個做流媒體應用的,可以理解爲國外的愛奇藝。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9b/9b7b623629ff4473526ea2c9be58022f.jpeg","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這張導圖右邊的是三個註冊中心中間件,Eureka就是Netflix公司開發的,Zookeeper這個功能比較強不是簡簡單單隻做註冊中心,它還可以做註冊中心和分佈式鎖,Consul則是一個Go語言開發的註冊中心中間件(也可以用作配置中心),這幾個裏面Eureka是這兩年用的比較多的,更早之前國內可能Zookeeper用的更多。","attrs":{}}]},{"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":"左邊第一個SpringCloudGateway則是網關中間件,由Spring開發,之前Netflix還有一個Zuul,也是用來做網關中間件的。","attrs":{}}]},{"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":"Feign/OpenFeign/Rabbon這三個我放在一塊說,Feign和OpenFeign其實是一個東西,OpenFeign是在Feign停更之後在其基礎上開發的,都是用來做服務間通信的,他們使用HTTP做通信協議,其實就是Spring中的RestTemplate,而Rabbon是一個做服務轉發策略的中間件,前文我們提到網關進行服務轉發時可以根據一定的策略比如:輪詢、隨機、權重這些策略,這塊其實就是Rabbon在起效,我們用Eureka的時候現在都是自帶Rabbon不需要額外去引入相關JAR包了,畢竟都是Netflix一家的東西。","attrs":{}}]},{"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":"SpringCloudConfig是Spring開發的配置中心中間件,功能比起其他的後起之秀比如攜程的Apollo,阿里的Nacos稍弱,所以不推薦用。","attrs":{}}]},{"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":"Hystrix也是Netflix公司開發的中間件,它的作用就是熔斷器,是一款非常經典的熔斷器中間件,這些中間件裏面它是相對來說最複雜的。","attrs":{}}]},{"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":"Sleuth是一款鏈路追蹤中間件,可以記錄你的請求會經過哪些鏈路,Zipkin則可以以可視化的形式將其表現出來。","attrs":{}}]},{"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":"上面這一套東西在我現在的公司裏面除了配置中心用的Apollo之外其他都用到了,算是比較流行的套件了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"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":"不同於上面的基本都是由Netflix開發的套件,下面我要介紹的套件是SpringCloudAlibaba套件,因爲是由Alibaba開發的所以一般都這樣稱呼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ec/ec626f0106c9e3fd12f001897f416d30.jpeg","alt":"掃盲帖:聊聊微服務與分佈式系統","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}},{"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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Nacos是一個多功能中間件,同時可以做註冊中心和配置中心,這個組件我自己也用過感覺還是蠻好用的,而且自帶一套比較美觀的可視化界面。","attrs":{}}]},{"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":"Duubo這個可能大家比較熟悉,原來是由阿里開發後來捐給Apache,現在是Apache的頂級項目之一,它是一個服務通信的組件,使用RPC通信方式。","attrs":{}}]},{"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":"Sentinel阿里開發的熔斷器,名字翻譯過來就是哨兵可以說是很貼切了,並且擁有鏈路追蹤的功能,還配有配套的可視化界面,可以說是一個人幹了Hystrix+Sleuth+Zipkin的活。","attrs":{}}]},{"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":"像阿里這套中間件我是比較推薦的,因爲第一套Netflix公司基本都已經宣佈停止更新了,比如Feign不更新之後Spring又接手進行二次開發搞了一個OpenFeign。","attrs":{}}]},{"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":"這裏再提一下Apollo,攜程開發的配置中心中間件,現在也有很多公司在用,我們公司也在用個人用起來感覺還是可以的,很方便上手也簡單。","attrs":{}}]},{"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":"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":"框架學的越多用的越多,你越會發現其實大部分都是大同小異,越能明白學好Java基礎的作用,越能知道設計模式、算法纔是一個軟件工程師必備的技能。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"看完三件事❤️","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},"content":[{"type":"text","text":"如果你覺得這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"","attrs":{}}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"點贊,轉發,有你們的 『點贊和評論』,纔是我創造的動力。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"關注公衆號 『 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"java爛豬皮","attrs":{}},{"type":"text","text":" 』,不定期分享原創知識。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"同時可以期待後續文章ing🚀","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":".關注後回覆【666】掃碼即可獲取學習資料包","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/68/68a35532863af30e801cac6e061f5853.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"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},"content":[{"type":"text","text":"鏈接:https://juejin.cn/post/6903325320904966151","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章