五分鐘閱讀阿里巴巴架構(gòu)師如何使用微服務(wù)框架搭建電商平臺全過程
本文你將學(xué)到什么?
本文將以原理 實戰(zhàn)的方式,首先對“微服務(wù)”相關(guān)的概念進(jìn)行知識點掃盲,然后開始手把手教你搭建這一整套的微服務(wù)系統(tǒng)。
這套微服務(wù)框架能干啥?
這套系統(tǒng)搭建完之后,那可就厲害了:
-
微服務(wù)架構(gòu) 。你的整個應(yīng)用程序?qū)徊鸱殖梢粋€個功能獨立的子系統(tǒng),獨立運行,系統(tǒng)與系統(tǒng)之間通過RPC接口通信。這樣這些系統(tǒng)之間的耦合度大大降低,你的系統(tǒng)將非常容易擴(kuò)展,團(tuán)隊協(xié)作效率提升了N個檔次。這種架構(gòu)通過眼下流行的SpringBoot和阿里巴巴吊炸天的Dubbo框架來實現(xiàn)。
容器化部署 。你的各個微服務(wù)將采用目前處于浪潮之巔的Docker來實現(xiàn)容器化部署,避免一切因環(huán)境引起的各種問題,讓你們團(tuán)隊的全部精力集中在業(yè)務(wù)開發(fā)上。
自動化構(gòu)建 。項目被微服務(wù)化后,各個服務(wù)之間的關(guān)系錯中復(fù)雜,打包構(gòu)建的工作量相當(dāng)可怕。不過沒關(guān)系,本文將借助Jenkins,幫助你一鍵自動化部署,從此你便告別了加班。
知識點1:微服務(wù)
微服務(wù)一次近幾年相當(dāng)火,成為程序猿飯前便后裝逼熱門詞匯,你不對它有所了解如何在程序猿裝逼圈子里混?下面我用最為通俗易懂的語言介紹它。
要講清楚微服務(wù),我先要從一個系統(tǒng)架構(gòu)的演進(jìn)過程講起。
單機(jī)結(jié)構(gòu)
我想大家最最最熟悉的就是單機(jī)結(jié)構(gòu),一個系統(tǒng)業(yè)務(wù)量很小的時候所有的代碼都放在一個項目中就好了,然后這個項目部署在一臺服務(wù)器上就好了。整個項目所有的服務(wù)都由這臺服務(wù)器提供。這就是單機(jī)結(jié)構(gòu)。
那么,單機(jī)結(jié)構(gòu)有啥缺點呢?我想缺點是顯而易見的,單機(jī)的處理能力畢竟是有限的,當(dāng)你的業(yè)務(wù)增長到一定程度的時候,單機(jī)的硬件資源將無法滿足你的業(yè)務(wù)需求。此時便出現(xiàn)了集群模式,往下接著看。
集群結(jié)構(gòu)
集群模式在程序猿界由各種裝逼解釋,有的讓你根本無法理解,其實就是一個很簡單的玩意兒,且聽我一一道來。
單機(jī)處理到達(dá)瓶頸的時候,你就把單機(jī)復(fù)制幾份,這樣就構(gòu)成了一個“集群”。集群中每臺服務(wù)器就叫做這個集群的一個“節(jié)點”,所有節(jié)點構(gòu)成了一個集群。每個節(jié)點都提供相同的服務(wù),那么這樣系統(tǒng)的處理能力就相當(dāng)于提升了好幾倍(有幾個節(jié)點就相當(dāng)于提升了這么多倍)。
但問題是用戶的請求究竟由哪個節(jié)點來處理呢?最好能夠讓此時此刻負(fù)載較小的節(jié)點來處理,這樣使得每個節(jié)點的壓力都比較平均。要實現(xiàn)這個功能,就需要在所有節(jié)點之前增加一個“調(diào)度者”的角色,用戶的所有請求都先交給它,然后它根據(jù)當(dāng)前所有節(jié)點的負(fù)載情況,決定將這個請求交給哪個節(jié)點處理。這個“調(diào)度者”有個牛逼了名字——負(fù)載均衡服務(wù)器。
集群結(jié)構(gòu)的好處就是系統(tǒng)擴(kuò)展非常容易。如果隨著你們系統(tǒng)業(yè)務(wù)的發(fā)展,當(dāng)前的系統(tǒng)又支撐不住了,那么給這個集群再增加節(jié)點就行了。但是,當(dāng)你的業(yè)務(wù)發(fā)展到一定程度的時候,你會發(fā)現(xiàn)一個問題——無論怎么增加節(jié)點,貌似整個集群性能的提升效果并不明顯了。這時候,你就需要使用微服務(wù)結(jié)構(gòu)了。
微服務(wù)結(jié)構(gòu)
先來對前面的知識點做個總結(jié)。
從單機(jī)結(jié)構(gòu)到集群結(jié)構(gòu),你的代碼基本無需要作任何修改,你要做的僅僅是多部署幾臺服務(wù)器,沒太服務(wù)器上運行相同的代碼就行了。但是,當(dāng)你要從集群結(jié)構(gòu)演進(jìn)到微服務(wù)結(jié)構(gòu)的時候,之前的那套代碼就需要發(fā)生較大的改動了。所以對于新系統(tǒng)我們建議,系統(tǒng)設(shè)計之初就采用微服務(wù)架構(gòu),這樣后期運維的成本更低。但如果一套老系統(tǒng)需要升級成微服務(wù)結(jié)構(gòu)的話,那就得對代碼大動干戈了。所以,對于老系統(tǒng)而言,究竟是繼續(xù)保持集群模式,還是升級成微服務(wù)架構(gòu),這需要你們的架構(gòu)師深思熟慮、權(quán)衡投入產(chǎn)出比。
OK,下面開始介紹所謂的微服務(wù)。
微服務(wù)就是將一個完整的系統(tǒng),按照業(yè)務(wù)功能,拆分成一個個獨立的子系統(tǒng),在微服務(wù)結(jié)構(gòu)中,每個子系統(tǒng)就被稱為“服務(wù)”。這些子系統(tǒng)能夠獨立運行在web容器中,它們之間通過RPC方式通信。
舉個例子,假設(shè)需要開發(fā)一個在線商城。按照微服務(wù)的思想,我們需要按照功能模塊拆分成多個獨立的服務(wù),如:用戶服務(wù)、產(chǎn)品服務(wù)、訂單服務(wù)、后臺管理服務(wù)、數(shù)據(jù)分析服務(wù)等等。這一個個服務(wù)都是一個個獨立的項目,可以獨立運行。如果服務(wù)之間有依賴關(guān)系,那么通過RPC方式調(diào)用。
這樣的好處有很多:
-
系統(tǒng)之間的耦合度大大降低,可以獨立開發(fā)、獨立部署、獨立測試,系統(tǒng)與系統(tǒng)之間的邊界非常明確,排錯也變得相當(dāng)容易,開發(fā)效率大大提升。
系統(tǒng)之間的耦合度降低,從而系統(tǒng)更易于擴(kuò)展。我們可以針對性地擴(kuò)展某些服務(wù)。假設(shè)這個商城要搞一次大促,下單量可能會大大提升,因此我們可以針對性地提升訂單系統(tǒng)、產(chǎn)品系統(tǒng)的節(jié)點數(shù)量,而對于后臺管理系統(tǒng)、數(shù)據(jù)分析系統(tǒng)而言,節(jié)點數(shù)量維持原有水平即可。
服務(wù)的復(fù)用性更高。比如,當(dāng)我們將用戶系統(tǒng)作為單獨的服務(wù)后,該公司所有的產(chǎn)品都可以使用該系統(tǒng)作為用戶系統(tǒng),無需重復(fù)開發(fā)。
那么問題來了,當(dāng)采用微服務(wù)結(jié)構(gòu)后,一個完整的系統(tǒng)可能有很多獨立的子系統(tǒng)組成,當(dāng)業(yè)務(wù)量漸漸發(fā)展起來之后,而這些子系統(tǒng)之間的關(guān)系將錯綜復(fù)雜,而且為了能夠針對性地增加某些服務(wù)的處理能力,某些服務(wù)的背后可能是一個集群模式,由多個節(jié)點構(gòu)成,這無疑大大增加了運維的難度。微服務(wù)的想法好是好,但開發(fā)、運維的復(fù)雜度實在是太高。為了解決這些問題,阿里巴巴的Dubbo就橫空出世了。
知識點2:Dubbo
Dubbo是一套微服務(wù)系統(tǒng)的協(xié)調(diào)者,在它這套體系中,一共有三種角色,分別是:服務(wù)提供者(下面簡稱提供者)、服務(wù)消費者(下面簡稱消費者)、注冊中心。
你在使用的時候需要將Dubbo的jar包引入到你的項目中,也就是每個服務(wù)都要引入Dubbo的jar包。然后當(dāng)這些服務(wù)初始化的時候,Dubbo就會將當(dāng)前系統(tǒng)需要發(fā)布的服務(wù)、以及當(dāng)前系統(tǒng)的IP和端口號發(fā)送給注冊中心,注冊中心便會將其記錄下來。這就是服務(wù)發(fā)布的過程。與此同時,也是在系統(tǒng)初始化的時候,Dubbo還會掃描一下當(dāng)前系統(tǒng)所需要引用的服務(wù),然后向注冊中心請求這些服務(wù)所在的IP和端口號。接下來系統(tǒng)就可以正常運行了。當(dāng)系統(tǒng)A需要調(diào)用系統(tǒng)B的服務(wù)的時候,A就會與B建立起一條RPC信道,然后再調(diào)用B系統(tǒng)上相應(yīng)的服務(wù)。
這,就是Dubbo的作用。
知識點3:容器化部署
當(dāng)我們使用了微服務(wù)架構(gòu)后,我們將一個原本完整的系統(tǒng),按照業(yè)務(wù)邏輯拆分成一個個可獨立運行的子系統(tǒng)。為了降低系統(tǒng)間的耦合度,我們希望這些子系統(tǒng)能夠運行在獨立的環(huán)境中,這些環(huán)境之間能夠相互隔離。
在Docker出現(xiàn)之前,若使用虛擬機(jī)來實現(xiàn)運行環(huán)境的相互隔離的話成本較高,虛擬機(jī)會消耗較多的計算機(jī)硬件/軟件資源。Docker不僅能夠?qū)崿F(xiàn)運行環(huán)境的隔離,而且能極大程度的節(jié)約計算機(jī)資源,它成為一種輕量級的“虛擬機(jī)”。
知識點4:自動化構(gòu)建
當(dāng)我們使用微服務(wù)架構(gòu)后,隨著業(yè)務(wù)的逐漸發(fā)展,系統(tǒng)之間的依賴關(guān)系會日益復(fù)雜,而且各個模塊的構(gòu)建順序都有所講究。對于一個小型系統(tǒng)來說,也許只有幾個模塊,那么你每次采用人肉構(gòu)建的方式也許并不感覺麻煩。但隨著系統(tǒng)業(yè)務(wù)的發(fā)展,你的系統(tǒng)之間的依賴關(guān)系日益復(fù)雜,子系統(tǒng)也逐漸增多,每次構(gòu)建一下你都要非常小心謹(jǐn)慎,稍有不慎整個服務(wù)都無法正常啟動。而且這些構(gòu)建的工作很low,但卻需要消耗大量的精力,這無疑降低了開發(fā)的效率。不過沒關(guān)系,Jenkins就是來幫助你解決這個問題的。
我們只需在Jenkins中配置好代碼倉庫、各個模塊的構(gòu)建順序和構(gòu)建命令,在以后的構(gòu)建中,只需要點擊“立即構(gòu)建”按鈕,Jenkins就會自動到你的代碼倉庫中拉取最新的代碼,然后根據(jù)你事先配置的構(gòu)建命令進(jìn)行構(gòu)建,最后發(fā)布到指定的容器中運行。你也可以讓Jenkins定時檢查代碼倉庫版本的變化,一旦發(fā)現(xiàn)變動就自動地開始構(gòu)建過程,并且讓Jenkins在構(gòu)建成功后給你發(fā)一封郵件。這樣你連“立即構(gòu)建”的按鈕也不需要按,就能全自動地完成這一切構(gòu)建過程。
實戰(zhàn)動手篇
1. 學(xué)習(xí)目標(biāo)
接下來我會帶著大家,以一個在線商城為例,搭建一套能夠自動化部署的微服務(wù)框架。這個框架能做如下幾件事情:
-
基于SpringBoot快速開發(fā) 。
我們將選擇目前熱度很高的SpringBoot,最大限度地降低配置復(fù)雜度,把大量的精力投入到我們的業(yè)務(wù)開發(fā)中來。
基于Dubbo的微服務(wù)化 。
我們會使用阿里巴巴的開源框架Dubbo,將我們的系統(tǒng)拆分成多個獨立的微服務(wù),然后用Dubbo來管理所有服務(wù)的發(fā)布和引用。有了Dubbo之后,調(diào)用遠(yuǎn)程服務(wù)就像調(diào)用一個本地函數(shù)一樣簡單,Dubbo會幫我們完成遠(yuǎn)程調(diào)用背后所需要的一切。
基于Docker的容器化部署 。
由于使用了微服務(wù)架構(gòu)后,我們的系統(tǒng)將會由很多子系統(tǒng)構(gòu)成。為了達(dá)到多個系統(tǒng)之間環(huán)境隔離的目的,我們可以將它們部署在多臺服務(wù)器上,可這樣的成本會比較高,而且每臺服務(wù)器的性能可能都沒有充分利用起來。所以我們很自然地想到了虛擬機(jī),在同一臺服務(wù)器上運行多個虛擬機(jī),從而實現(xiàn)環(huán)境的隔離,每個虛擬機(jī)上運行獨立的服務(wù)。然而虛擬機(jī)的隔離成本依舊很高,因為它需要占用服務(wù)器較多的硬件資源和軟件資源。所以,在微服務(wù)結(jié)構(gòu)下,要實現(xiàn)服務(wù)環(huán)境的隔離,Docker是最佳選擇。它比虛擬機(jī)更加輕量級,占用資源較少,而且能夠?qū)崿F(xiàn)快速部署。
基于Jenkins的自動化構(gòu)建 。
當(dāng)我們采用了微服務(wù)架構(gòu)后,我們會發(fā)現(xiàn)這樣一個問題。整個系統(tǒng)由許許多多的服務(wù)構(gòu)成,這些服務(wù)都需要運行在單獨的容器中,那么每次發(fā)布的復(fù)雜度將非常高。首先你要搞清楚這些服務(wù)之間的依賴關(guān)系、啟動的先后順序,然后再將多個子系統(tǒng)挨個編譯、打包、發(fā)布。這些操作技術(shù)難度低,卻又容易出錯。那么有什么工具能夠幫助我們解決這些問題呢?答案就是——Jenkins。
它是一款自動化構(gòu)建的工具,簡單的來說,就是我們只需要在它的界面上按一個按鈕,就可以實現(xiàn)上述一系列復(fù)雜的過程。文章中涉及到的微服務(wù)、dubbo,等技術(shù)均分享在群619881427已錄制成視頻,可免費下載。如有需要可以加入進(jìn)來一起學(xué)習(xí)交流
2. 項目背景介紹
本文我以一個大家都非常熟悉的在線商城作為例子,一步步教大家如何搭建微服務(wù)框架,它有如下功能:
-
產(chǎn)品管理:產(chǎn)品的增刪改查。
訂單管理:訂單的增刪改查、購物車功能。
用戶管理:用戶的登錄、注冊、權(quán)限管理、收貨地址等等。
數(shù)據(jù)分析:提供對本系統(tǒng)數(shù)據(jù)分析的功能。
注意:本文的IDE使用的是intelliJ IDEA,推薦大家也用這個,用了都說好,用了你就會愛上它。
3. 創(chuàng)建項目的組織結(jié)構(gòu)
在動手之前,我先來說一說這一步的目標(biāo):
-
創(chuàng)建一個Maven Project,命名為“Gaoxi”
這個Project由多個Module構(gòu)成,每個Module對應(yīng)著“微服務(wù)”的一個子系統(tǒng),可獨立運行,是一個獨立的項目。
這也是目前主流的項目組織形式,即多模塊項目。
-
在Gaoxi這個項目下創(chuàng)建各個子模塊,每個自模塊都是一個獨立的SpringBoot項目:
Gaoxi-User
用戶服務(wù)
Gaoxi-Order
訂單服務(wù)
Gaoxi-Product
產(chǎn)品服務(wù)
Gaoxi-Analysis
數(shù)據(jù)分析服務(wù)
Gaoxi-Controller
本系統(tǒng)的控制層,和以往三層結(jié)構(gòu)中的Controller層的作用一樣,都是用作請求調(diào)度,只不過在微服務(wù)架構(gòu)中,我們將它抽象成一個單獨的系統(tǒng),可以獨立運行。
Gaoxi-Common-Service-Facade
它處于本系統(tǒng)的最底層,被所有模塊依賴,一些公用的類庫都放在這里。
Gaoxi-Redis
我們將Redis封裝成一個單獨的服務(wù),運行在獨立的容器中,當(dāng)哪一個模塊需要使用Redis的時候,僅需要引入該服務(wù)即可,就免去了各種繁瑣的、重復(fù)的配置。而這些配置均在Gaoxi-Redis系統(tǒng)中完成了。
開始動手
3.1 創(chuàng)建Project
-
New一個Project
選擇Spring Initializr
-
設(shè)置groupId、artifactId、version
-
Project創(chuàng)建完畢!接下來在Project下面創(chuàng)建Module
3.2 創(chuàng)建Module
-
在Project上New Module
-
和剛才一樣,選擇Spring Initializr,設(shè)置groupId、artifactId、version
依次創(chuàng)建好所有的Module,如下圖所示:
3.3 構(gòu)建模塊的依賴關(guān)系
目前為止,模塊之間沒有任何聯(lián)系,下面我們要通過pom文件來指定它們之間的依賴關(guān)系,依賴關(guān)系如下圖所示:
Gaoxi-User、Gaoxi-Analysis、Gaoxi-Product、Gaoxi-Order這四個系統(tǒng)相當(dāng)于以往三層結(jié)構(gòu)的Service層,提供系統(tǒng)的業(yè)務(wù)邏輯,只不過在微服務(wù)結(jié)構(gòu)中,Service層的各個模塊都被抽象成一個個單獨的子系統(tǒng),它們提供RPC接口供上面的Gaoxi-Controller調(diào)用。它們之間的調(diào)用由Dubbo來完成,所以它們的pom文件中并不需要作任何配置。而這些模塊和Gaoxi-Common-Service-Facade之間是本地調(diào)用,因此需要將Gaoxi-Common-Service-Facade打成jar包,并讓這些模塊依賴這個jar,因此就需要在所有模塊的pom中配置和Gaoxi-Common-Service-Facade的依賴關(guān)系。
此外,為了簡化各個模塊的配置,我們將所有模塊的通用依賴放在Project的pom文件中,然后讓所有模塊作為Project的子模塊。這樣子模塊就可以從父模塊中繼承所有的依賴,而不需要自己再配置了。
下面開始動手:
-
首先將Common-Service-Facade的打包方式設(shè)成jar
當(dāng)打包這個模塊的時候,Maven會將它打包成jar,并安裝在本地倉庫中。這樣其他模塊打包的時候就可以引用這個jar。
<groupId>com.gaoxi</groupId><artifactId>gaoxi-common-service-facade</artifactId><version>0.0.1</version><packaging>jar</packaging>
將其他模塊的打包方式設(shè)為war
除了Gaoxi-Common-Service-Facade外,其他模塊都是一個個可獨立運行的子系統(tǒng),需要在web容器中運行,所以我們需要將這些模塊的打包方式設(shè)成war
<groupId>com.gaoxi</groupId><artifactId>gaoxi-user</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging>
在總pom中指定子模塊
modules標(biāo)簽指定了當(dāng)前模塊的子模塊是誰,但是僅在父模塊的pom文件中指定子模塊還不夠,還需要在子模塊的pom文件中指定父模塊是誰。
<modules><module>Gaoxi-Analysis</module><module>Gaoxi-Order</module><module>Gaoxi-Product</module><module>Gaoxi-User</module><module>Gaoxi-Redis</module><module>Gaoxi-Controller</module><module>Gaoxi-Common-Service-Facade</module></modules>
在子模塊中指定父模塊
<parent><groupId>com.gaoxi</groupId><artifactId>gaoxi</artifactId><version>0.0.1-SNAPSHOT</version><relativePath>../pom.xml</relativePath></parent>
到此為止,模塊的依賴關(guān)系配置完畢!但要注意模塊打包的順序。由于所有模塊都依賴于Gaoxi-Common-Servie-Facade模塊,因此在構(gòu)建模塊時,首先需要編譯、打包、安裝Gaoxi-Common-Servie-Facade,將它打包進(jìn)本地倉庫中,這樣上層模塊才能引用到。當(dāng)該模塊安裝完畢后,再構(gòu)建上層模塊。否則在構(gòu)建上層模塊的時候會出現(xiàn)找不到Gaoxi-Common-Servie-Facade中類庫的問題。
3.4 在父模塊的pom中添加所有子模塊公用的依賴
<dependencies><!– Spring Boot –><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!– Spring MVC –><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!– Spring Boot Test –><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!– MyBatis –><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.1</version></dependency><!– Mysql –><dependency><groupId>mysql</groupId><artifactId>mysql-connector-Java</artifactId><scope>runtime</scope></dependency><!– Dubbo –><dependency><groupId>io.dubbo.springboot</groupId><artifactId>spring-boot-starter-dubbo</artifactId><version>1.0.0</version></dependency><!– gaoxi-common-service-Facade –><dependency><groupId>com.gaoxi</groupId><artifactId>gaoxi-common-service-facade</artifactId><version>0.0.1</version></dependency><!– AOP –><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!– guava –><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.3-jre</version></dependency></dependencies>
當(dāng)父模塊的pom中配置了公用依賴后,子模塊的pom文件將非常簡潔,如下所示:
<groupId>com.gaoxi</groupId><artifactId>gaoxi-user</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>gaoxi-user</name><parent><groupId>com.gaoxi</groupId><artifactId>gaoxi</artifactId><version>0.0.1-SNAPSHOT</version><relativePath>../pom.xml</relativePath></parent>
當(dāng)項目的結(jié)構(gòu)搭建完成之后,接下來你需要配置Docker環(huán)境,并將這些項目打包進(jìn)容器中,驗證下是否能正常啟動。
4. 創(chuàng)建Docker容器
4.1 安裝Docker
在使用Docker之前,你當(dāng)然先要安裝Docker,安裝過程較為簡單,基本上就是傻瓜式操作,這里就不作過多介紹了,你可以在Docker的官網(wǎng)下載相應(yīng)系統(tǒng)的安裝包。
https://www.docker.com/
4.2 獲取Tomcat鏡像
在微服務(wù)架構(gòu)中,一個完整的系統(tǒng)被拆分成了多個被稱為“微服務(wù)”的子系統(tǒng),這些子系統(tǒng)可以獨立運行在Web容器中。所以我們需要為這些系統(tǒng)提供運行的Web容器,這里我們選擇大家較為熟悉的Tomcat。
我們知道,Tomcat依賴于Java環(huán)境,安裝Tomcat之前要進(jìn)行一系列環(huán)境的配置:安裝Java、配置環(huán)境變量、安裝Tomcat等等。這些操作還是有些繁瑣的。不過沒關(guān)系,當(dāng)使用了Docker之后,這些過程都可以輕而易舉地完成。
我們只需從Docker Hub上找到Tomcat的鏡像資源,然后從上面拉取下來就可以使用。你可以使用Tomcat官方的鏡像,也可以使用我發(fā)布在Docker Hub上的Tomcat鏡像。
注意點:推薦使用我的Tomcat鏡像資源chaimm/tomcat,因為這個鏡像中除了配置Tomcat的安裝環(huán)境以外,還有一些本項目中要用到的Jenkins相關(guān)的配置。
采用如下命令從Docker Hub上拉取鏡像:
docker pull chaimm/tomcat:1.1
簡單解釋下,docker pull是從從Docker Hub上拉取鏡像的命令,后面的chaimm/tomcat是鏡像的名稱,:1.1是鏡像的版本號。目前這個鏡像的最新版本號是1.1,推薦大家拉取這個。
4.3 創(chuàng)建Tomcat容器
這里再簡單介紹下“鏡像”和“容器”的關(guān)系。
“鏡像”就好比是面向?qū)ο笾械摹邦悺?,“容器”就好比“類”?chuàng)建的“對象”。在面向?qū)ο笾?,“類”定義了各種屬性,“類”可以實例化出多個“對象”;而在Docker中,“鏡像”定義了各種配置信息,它可以實例化出多個“容器”?!叭萜鳌本褪且慌_可以運行的“虛擬機(jī)”。
接下來我們需要為所有的微服務(wù)創(chuàng)建各自的容器:
-
gaoxi-user
gaoxi-product
gaoxi-order
gaoxi-analysis
gaoxi-controller
gaoxi-redis
以創(chuàng)建gaoxi-user容器為例,采用如下命令創(chuàng)建容器:
docker run –name gaoxi-user-1 -p 8082:8080 -v /usr/web/gaoxi-log:/opt/tomcat/gaoxi-log chaimm/tomcat:1.1
–name:指定容器的名字
-p:指定容器的端口映射
-p 8082:8080 表示將容器的8080端口映射到宿主機(jī)的8082端口上
-
-v:指定容器數(shù)據(jù)卷的映射
xxx:yyy 表示將容器yyy目錄映射到宿主機(jī)的xxx目錄上,從而訪問宿主機(jī)的xxx目錄就相當(dāng)于訪問容器的yyy目錄。
chaimm/tomcat:1.1:表示容器所對應(yīng)的鏡像。
這條命令執(zhí)行成功后,你就可以通過 你的IP:8082
訪問到gaoxi-user-1容器的tomcat了。如果你看到了那只眼熟了貓,那就說明容器啟動成功了!
接下來,你需要按照上面的方法,給剩下幾個系統(tǒng)創(chuàng)建好Tomcat容器。
注意點:這里要注意的是,你需要給這些Tomcat容器指定不同的端口號,防止端口號沖突。當(dāng)然,在實際開發(fā)中,你并不需要將容器的8080端口映射到宿主機(jī)上,這里僅僅是為了驗證容器是否啟動成功才這么做的。文章中涉及到的微服務(wù)、dubbo,等技術(shù)均分享在群619881427已錄制成視頻,可免費下載。如有需要可以加入進(jìn)來一起學(xué)習(xí)交流
5. 整合Dubbo
5.1 創(chuàng)建Zookeeper容器
Dubbo一共定義了三種角色,分別是:服務(wù)提供者、服務(wù)消費者、注冊中心。注冊中心是服務(wù)提供者和服務(wù)消費者的橋梁,服務(wù)消費者會在初始化的時候?qū)⒆约旱腎P和端口號發(fā)送給注冊中心,而服務(wù)消費者通過注冊中心知道服務(wù)提供者的IP和端口號。
在Dubbo中,注冊中心有多種選擇,Dubbo最為推薦的即為ZooKeeper,本文采用ZooKeepeer作為Dubbo的注冊中心。
創(chuàng)建ZooKeeper容器也較為簡單,大家可以直接使用我創(chuàng)建的ZooKeeper鏡像,通過如下命令即可下載鏡像:
docker pull chaimm/zookeeper-dubbo:1.0
該鏡像中不僅運行了一個zookeeper,還運行了一個擁有dubbo-admin項目的tomcat。dubbo-admin是Dubbo的一個可視化管理工具,可以查看服務(wù)的發(fā)布和引用的情況。
使用如下命令啟動容器:
docker run –name zookeeper-debug -p 2182:2181 -p 10000:8080 chaimm/zookeeper-dubbo:1.0
-p 2182:2181:將容器的2181端口映射到宿主機(jī)的2182端口上,該端口是ZooKeeper的端口號。
-p 10000:8080:將容器的8080端口映射到宿主機(jī)的10000端口上,該端口是Dubbo-Admin所在Tomcat的端口號。
啟動成功后,你就可以通過 你的IP:10000/dubbo-admin-2.8.4/
訪問到Dubbo-Admin,如下圖所示:
5.2 父pom文件中引入dubbo依賴
<!– Spring Boot Dubbo 依賴 –><dependency><groupId>io.dubbo.springboot</groupId><artifactId>spring-boot-starter-dubbo</artifactId><version>1.0.0</version></dependency>
5.3 發(fā)布服務(wù)
假設(shè),我們需要將Gaoxi-User項目中的UserService發(fā)布成一項RPC服務(wù),供其他系統(tǒng)遠(yuǎn)程調(diào)用,那么我們究竟該如何借助Dubbo來實現(xiàn)這一功能呢?
-
在Gaoxi-Common-Service-Facade中定義UserService的接口
由于服務(wù)的發(fā)布和引用都依賴于接口,但服務(wù)的發(fā)布方和引用方在微服務(wù)架構(gòu)中往往不在同一個系統(tǒng)中,所以需要將需要發(fā)布和引用的接口放在公共類庫中,從而雙方都能夠引用。接口如下所示:
public interface UserService { public UserEntity login(LoginReq loginReq);}
在Gaoxi-User中定義接口的實現(xiàn)
在實現(xiàn)類上需要加上Dubbo的@Service注解,從而Dubbo會在項目啟動的時候掃描到該注解,將它發(fā)布成一項RPC服務(wù)。
@Service(version = “1.0.0”)public class UserServiceImpl implements UserService { @Overridepublic UserEntity login(LoginReq loginReq) { // 具體的實現(xiàn)代碼}}
在Gaoxi-User的application.properties中配置服務(wù)提供者的信息
spring.dubbo.application.name=user-provider # 本服務(wù)的名稱spring.dubbo.registry.address=zookeeper://IP:2182 # ZooKeeper所在服務(wù)器的IP和端口號spring.dubbo.protocol.name=dubbo # RPC通信所采用的協(xié)議spring.dubbo.protocol.port=20883 # 本服務(wù)對外暴露的端口號spring.dubbo.scan=com.gaoxi.user.service # 服務(wù)實現(xiàn)類所在的路徑
按照上面配置完成后,當(dāng)Gaoxi-User系統(tǒng)初始化的時候,就會掃描spring.dubbo.scan所指定的路徑下的@Service注解,該注解標(biāo)識了需要發(fā)布成RPC服務(wù)的類。Dubbo會將這些類的接口信息 本服務(wù)器的IP spring.dubbo.protocol.port所指定的端口號發(fā)送給Zookeeper,Zookeeper會將這些信息存儲起來。
這就是服務(wù)發(fā)布的過程,下面來看如何引用一項RPC服務(wù)。
5.4 引用服務(wù)
假設(shè),Gaoxi-Controller需要調(diào)用Gaoxi-User 提供的登錄功能,此時它就需要引用UserService這項遠(yuǎn)程服務(wù)。下面來介紹服務(wù)引用的方法。
-
聲明需要引用的服務(wù)
引用服務(wù)非常簡單,你只需要在引用的類中聲明一項服務(wù),然后用@Reference標(biāo)識,如下所示:
@RestControllerpublic class UserControllerImpl implements UserController { @Reference(version = “1.0.0”) private UserService userService;@Overridepublic Result login(LoginReq loginReq, HttpServletResponse httpRsp) { // 登錄鑒權(quán)UserEntity userEntity = userService.login(loginReq);}}
在Gaoxi-Controller的application.properties中配置服務(wù)消費者的信息
spring.dubbo.application.name=controller-consumer # 本服務(wù)的名稱spring.dubbo.registry.address=zookeeper://IP:2182 # zookeeper所在服務(wù)器的IP和端口號spring.dubbo.scan=com.gaoxi # 引用服務(wù)的路徑
上述操作完成后,當(dāng)Gaoxi-Controller初始化的時候,Dubbo就會掃描spring.dubbo.scan所指定的路徑,并找到所有被@Reference修飾的成員變量;然后向Zookeeper請求該服務(wù)所在的IP和端口號。當(dāng)調(diào)用userService.login()的時候,Dubbo就會向Gaoxi-User發(fā)起請求,完成調(diào)用的過程。這個調(diào)用過程是一次RPC調(diào)用,但作為程序猿來說,這和調(diào)用一個本地函數(shù)沒有任何區(qū)別,遠(yuǎn)程調(diào)用的一切都由Dubbo來幫你完成。這就是Dubbo的作用。
6. 自動化構(gòu)建
Jenkins是一個自動化構(gòu)建工具,它可以幫助我們擺脫繁瑣的部署過程,我們只需要在一開始配置好構(gòu)建策略,以后部署只需要一鍵完成。
6.1 創(chuàng)建Jenkins容器
Jenkins采用Java開發(fā),也需要Java環(huán)境,但我們使用Docker后,一切都采用容器化部署,Jenkins也不例外。
-
拉取鏡像
這里我們使用Jenkins官方提供的鏡像,大家只需執(zhí)行如下命令拉取即可:
docker pull docker.io/jenkins/jenkins
啟動容器
由于Jenkins運行在Tomcat容器中,因此我們將容器的8080端口映射到宿主機(jī)的10080端口上:
docker run –name jenkins -p 10080:8080 docker.io/jenkins/jenkins
初始化Jenkins
然后你需要訪問
IP:10080
Jenkins會帶著你進(jìn)行一系列的初始化設(shè)置,你只要跟著它一步步走就行了,比較傻瓜式。
6.2 在Jenkins中創(chuàng)建項目
接下來我們要做的是,在Jenkins中為每一個服務(wù)創(chuàng)建一個項目,每個項目中定義了構(gòu)建的具體流程。由于我們將整個項目分成了6個微服務(wù),所以我們需要在Jenkins中分別為這6個服務(wù)創(chuàng)建項目。那句開始吧~
點擊頁面左側(cè)的“新建”按鈕:
-
輸入項目名稱gaoxi-user,選擇“構(gòu)建一個Maven項目”,然后點擊“OK”:
-
配置Git倉庫
選擇Git,然后輸入本項目Git倉庫的URL,并在Credentials中輸入Git的用戶名和密碼,如下圖所示:
-
構(gòu)建觸發(fā)器
選擇第一項,如下圖所示:
-
Pre Step
Pre Step會在正式構(gòu)建前執(zhí)行,由于所有項目都依賴于Gaoxi-Common-Service—Facade,因此在項目構(gòu)建前,需要將它安裝到本地倉庫,然后才能被當(dāng)前項目正確依賴。
Build
然后就是正式構(gòu)建的過程,填寫如下信息即可:
因此,在Pre Step中填寫如下信息:
OK,Gaoxi-User的構(gòu)建過程就配置完成了。當(dāng)我們點擊“立即構(gòu)建”按鈕時,Jenkins首先會從我們指定的Git倉庫中拉取代碼,然后執(zhí)行Pre Step中的Maven命令,將Gaoxi-Common-Serivce-Facade打包安裝到本地倉庫。然后執(zhí)行Build過程,將Gaoxi-User進(jìn)行編譯打包。
6.3 遠(yuǎn)程部署
-
下載插件
首先你需要下載Deploy Plugin
安裝插件
在系統(tǒng)管理–>插件管理–>高級上傳deploy.hpi進(jìn)行安裝。
在父項目的pom文件中增加遠(yuǎn)程部署插件:
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.6.5</version>
<configuration>
<container>
<!– 指明使用的tomcat服務(wù)器版本 –>
<containerId>tomcat8x</containerId>
<type>remote</type>
</container>
<configuration>
<type>runtime</type>
<cargo.remote.username>Tomcat的用戶名</cargo.remote.username>
<cargo.remote.password>Tomcat的密碼</cargo.remote.password>
</configuration>
</configuration>
<executions>
<execution>
<phase>deploy</phase>
<goals>
<goal>redeploy</goal>
</goals>
</execution>
</executions></plugin>為Tomcat設(shè)置用戶名和密碼
修改gaoxi-user容器中tomcat的tomcat-users.xml文件,增加tomcat的manager用戶
注意:如果你使用了chaimm/tomcat鏡像,那么其中Tomcat配置都已經(jīng)完成,默認(rèn)用戶名:admin、默認(rèn)密碼:jishimen2019。強(qiáng)烈建議修改用戶名和密碼。
-
修改Jenkins中g(shù)aoxi-user的配置
在“構(gòu)建后操作”中增加如下配置:
– WAR/EAR files:表示你需要發(fā)布的war包- Containers:配置目標(biāo)Tomcat的用戶名和密碼
7. Maven的profile功能
在實際開發(fā)中,我們的系統(tǒng)往往有多套環(huán)境構(gòu)成,如:開發(fā)環(huán)境、測試環(huán)境、預(yù)發(fā)環(huán)境、生產(chǎn)環(huán)境。而不同環(huán)境的配置各不相同。如果我們只有一套配置,那么當(dāng)系統(tǒng)從一個環(huán)境遷移到另一個環(huán)境的時候,就需要通過修改代碼來更換配置,這樣無疑增加了工作的復(fù)雜度,而且易于出錯。但好在Maven提供了profile功能,能幫助我們解決這一個問題。
-
父項目的pom中添加profile元素
首先,我們需要在總pom的中添加多套環(huán)境的信息,如下所示:
<profiles>
<profile>
<id>dev</id>
<properties>
<profileActive>dev</profileActive>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
<properties>
<profileActive>test</profileActive>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profileActive>prod</profileActive>
</properties>
</profile></profiles> 父項目的pom中添加resource元素
resource標(biāo)識了不同環(huán)境下需要打包哪些配置文件。
<resources>
<resource>
<!– 標(biāo)識配置文件所在的目錄 –>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<!– 構(gòu)建時將這些配置文件全都排除掉 –>
<excludes>
<exclude>application.properties</exclude>
<exclude>application-dev.properties</exclude>
<exclude>application-test.properties</exclude>
<exclude>application-prod.properties</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<!– 標(biāo)識構(gòu)建時所需要的配置文件 –>
<includes>
<include>application.properties</include>
<!– ${profileActive}這個值會在maven構(gòu)建時傳入 –>
<include>application-${profileActive}.properties</include>
</includes>
</resource></resources>父項目的pom中添加插件maven-resources-plugin
該插件用來在Maven構(gòu)建時參數(shù)替換
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration></plugin>在子項目中創(chuàng)建配置
分別為dev環(huán)境、test環(huán)境、prod環(huán)境創(chuàng)建三套配置,application.proerpties中存放公用的配置。
-
在application.properties中添加spring.profiles.active=@profileActive@
spring.profiles.active=@profileActive@
修改Jenkins的配置
在所有Jenkins中所有Maven命令的末尾添加 -P test
,在打包的時候-P后面的參數(shù)將會作為@profileActive@的值傳入系統(tǒng)中,從而根據(jù)該值打包相應(yīng)的application-{profileActive}.properties文件。文章中涉及到的微服務(wù)、dubbo,等技術(shù)均分享在群619881427已錄制成視頻,可免費下載。如有需要可以加入進(jìn)來一起學(xué)習(xí)交流
8. 開發(fā)流程
到此為止,所有準(zhǔn)備工作都已經(jīng)完成,接下來就可以進(jìn)入代碼開發(fā)階段。下面我以一個例子,帶著大家感受下有了這套微服務(wù)框架后,我們的開發(fā)流程究竟有了哪些改變?下面以開發(fā)一個用戶登錄功能為例,介紹下使用本框架之后開發(fā)的流程。
8.1 開發(fā)目標(biāo)
-
在Gaoxi-User系統(tǒng)中實現(xiàn)登錄的業(yè)務(wù)邏輯,并發(fā)布成RPC服務(wù)
在Gaoxi-Controller中遠(yuǎn)程調(diào)用登錄服務(wù),并向前端提供登錄的REST接口
8.2 開發(fā)登錄服務(wù)
首先需要在Gaoxi-Common-Service-Facade中創(chuàng)建UserService接口,并在其中聲明登錄的抽象函數(shù)。
public interface UserService { public UserEntity login(LoginReq loginReq);}
PS:為什么要將UserService放在Gaoxi-Common-Service-Facade中?
然后在Gaoxi-User中開發(fā)UserService的實現(xiàn)——UserServiceImpl。
UserServiceImpl上必須要加上Dubbo的@Service注解,從而告訴Dubbo,在本項目初始化的時候需要將這個類發(fā)布成一項服務(wù),供其他系統(tǒng)調(diào)用。
@Service(version = “1.0.0”)@org.springframework.stereotype.Servicepublic class UserServiceImpl implements UserService { @Autowiredprivate UserDAO userDAO; @Overridepublic UserEntity login(LoginReq loginReq) { // 校驗參數(shù)checkParam(loginReq); // 創(chuàng)建用戶查詢請求UserQueryReq userQueryReq = buildUserQueryReq(loginReq); // 查詢用戶List<UserEntity> userEntityList = userDAO.findUsers(userQueryReq); // 查詢失敗if (CollectionUtils.isEmpty(userEntityList)) { throw new CommonBizException(ExpCodeEnum.LOGIN_FAIL);} // 查詢成功return userEntityList.get(0);}}
8.3 引用登錄服務(wù)
當(dāng)UserService開發(fā)完畢后,接下來Gaoxi-Controller需要引用該服務(wù),并向前端提供一個登錄的REST接口。
若要使用userService中的函數(shù),僅需要在userService上添加@Reference注解,然后就像調(diào)用本地函數(shù)一樣使用userService即可。Dubbo會幫你找到UserService服務(wù)所在的IP和端口號,并發(fā)送調(diào)用請求。但這一切對于程序猿來說是完全透明的。
@RestControllerpublic class UserControllerImpl implements UserController { @Reference(version = “1.0.0”)private UserService userService; /** * 登錄 * @param loginReq 登錄請求參數(shù) * @param httpRsp HTTP響應(yīng) * @return 登錄是否成功 */@GetMapping(“/login”)@Overridepublic Result login(LoginReq loginReq, HttpServletResponse httpRsp) { // 登錄鑒權(quán)UserEntity userEntity = userService.login(loginReq); // 登錄成功doLoginSuccess(userEntity, httpRsp); return Result.newSuccessResult();}}
8.4 自動構(gòu)建服務(wù)
上面的代碼完成后,接下來你需要將代碼提交至你的Git倉庫。接下來就是自動化部署的過程了。
你需要進(jìn)入Jenkins,由于剛才修改了Gaoxi-User和Gaoxi-Controller的代碼,因此你需要分別構(gòu)建這兩個項目。
接下來Jenkins會自動從你的Git倉庫中拉取最新的代碼,然后依次執(zhí)行Pre Step、Build、構(gòu)建后操作的過程。由于我們在Pre Step中設(shè)置了編譯Gaoxi-Common-Service-Facade,因此Jenkins首先會將其安裝到本地倉庫;然后再執(zhí)行Build過程,構(gòu)建Gaoxi-User,并將其打包成war包。最后將執(zhí)行“構(gòu)建后操作”,將war包發(fā)布到相應(yīng)的tomcat容器中。
至此,整個發(fā)布流程完畢!
8.5 查看服務(wù)的狀態(tài)
當(dāng)Jenkins構(gòu)建完成后,我們可以登錄Dubbo-Admin查看服務(wù)發(fā)布和引用的狀態(tài)。
當(dāng)我們搜索UserService服務(wù)后,可以看到,該服務(wù)的提供者已經(jīng)成功發(fā)布了服務(wù):
點擊“消費者”我們可以看到,該服務(wù)已經(jīng)被controller-consumer成功訂閱:
9. 總結(jié)
總結(jié)一下,這套框架有如下優(yōu)勢:
-
微服務(wù)架構(gòu)
我們借助于SpringBoot和Dubbo實現(xiàn)了微服務(wù)架構(gòu)。微服務(wù)架構(gòu)的理念就是將一個原本龐大、復(fù)雜的系統(tǒng),按照業(yè)務(wù)功能拆分成一個個具有獨立功能、可以獨立運行的子系統(tǒng),系統(tǒng)之間若有依賴,則通過RPC接口通信。從而最大限度地降低了系統(tǒng)之間的耦合度,從而更加易于擴(kuò)展、更加易于維護(hù)。
容器化部署
我們借助于Docker實現(xiàn)了容器化部署。容器能夠幫助我們屏蔽不同環(huán)境下的配置問題,使得我們只需要有一個Dockerfile文件,就可以處處運行。和虛擬機(jī)一樣,Docker也擁有環(huán)境隔離的能力,但比虛擬機(jī)更加輕量級,由于每個容器僅僅是一條進(jìn)程,因此它可以達(dá)到秒級的啟動速度。
自動化構(gòu)建
我們借助于Jenkins實現(xiàn)了所有項目的自動化構(gòu)建與部署。我們只需要點擊“立即構(gòu)建”這個按鈕,Jenkins就可以幫助我們梳理好錯綜復(fù)雜的項目依賴關(guān)系,準(zhǔn)確無誤地完成構(gòu)建,并將war包發(fā)送到相應(yīng)的web容器中。在啟動的過程中,Dubbo會掃描當(dāng)前項目所需要發(fā)布和引用的服務(wù),將所需要發(fā)布的服務(wù)發(fā)布到ZooKeeper上,并向ZooKeeper訂閱所需的服務(wù)。
有了Jenkins之后,這一切都是自動化完成。也許你并沒有太強(qiáng)烈地感受到Jenkins所帶來的便利。但是你想一想,對于一個具有錯綜復(fù)雜的依賴關(guān)系的微服務(wù)系統(tǒng)而言,如果每個服務(wù)的構(gòu)建都需要你手動完成的話,你很快就會崩潰,你大把的時間將會投入在無聊但又容易出錯的服務(wù)構(gòu)建上。而Jenkins的出現(xiàn)能讓這一切自動化完成。