0基礎教你搭建一套可自動化構建的微服務框架(SpringBoot+Dubbo+Docker+Jenkins)

本文你將學到什麼?

本文將以原理+實戰的方式,首先對「微服務」相關的概念進行知識點掃盲,而後開始手把手教你搭建這一整套的微服務系統。前端

項目完整源碼下載

https://github.com/bz51/Sprin...java

image

這套微服務框架能幹啥?

這套系統搭建完以後,那可就厲害了:mysql

  • 微服務架構。你的整個應用程序將會被拆分紅一個個功能獨立的子系統,獨立運行,系統與系統之間經過RPC接口通訊。這樣這些系統之間的耦合度大大下降,你的系統將很是容易擴展,團隊協做效率提高了N個檔次。這種架構經過眼下流行的SpringBoot和阿里巴巴吊炸天的Dubbo框架來實現。
  • 容器化部署。你的各個微服務將採用目前處於浪潮之巔的Docker來實現容器化部署,避免一切因環境引發的各類問題,讓大家團隊的所有精力集中在業務開發上。
  • 自動化構建。項目被微服務化後,各個服務之間的關係錯中複雜,打包構建的工做量至關可怕。不過不要緊,本文將藉助Jenkins,幫助你一鍵自動化部署,今後你便告別了加班。

知識點掃盲篇

咳咳,敲黑板啦!筆記趕忙記起來,課後我要檢查的!檢查不合格的同窗放學後留下來!git

知識點1:微服務

微服務一次近幾年至關火,成爲程序猿飯前便後裝逼熱門詞彙,你不對它有所瞭解如何在程序猿裝逼圈子裏混?下面我用最爲通俗易懂的語言介紹它。github

要講清楚微服務,我先要從一個系統架構的演進過程講起。web

單機結構

我想你們最最最熟悉的就是單機結構,一個系統業務量很小的時候全部的代碼都放在一個項目中就行了,而後這個項目部署在一臺服務器上就行了。整個項目全部的服務都由這臺服務器提供。這就是單機結構。
那麼,單機結構有啥缺點呢?我想缺點是顯而易見的,單機的處理能力畢竟是有限的,當你的業務增加到必定程度的時候,單機的硬件資源將沒法知足你的業務需求。此時便出現了集羣模式,往下接着看。redis

集羣結構

集羣模式在程序猿界由各類裝逼解釋,有的讓你根本沒法理解,其實就是一個很簡單的玩意兒,且聽我一一道來。spring

單機處理到達瓶頸的時候,你就把單機複製幾份,這樣就構成了一個「集羣」。集羣中每臺服務器就叫作這個集羣的一個「節點」,全部節點構成了一個集羣。每一個節點都提供相同的服務,那麼這樣系統的處理能力就至關於提高了好幾倍(有幾個節點就至關於提高了這麼多倍)。sql

但問題是用戶的請求究竟由哪一個節點來處理呢?最好可以讓此時此刻負載較小的節點來處理,這樣使得每一個節點的壓力都比較平均。要實現這個功能,就須要在全部節點以前增長一個「調度者」的角色,用戶的全部請求都先交給它,而後它根據當前全部節點的負載狀況,決定將這個請求交給哪一個節點處理。這個「調度者」有個牛逼了名字——負載均衡服務器。docker

集羣結構的好處就是系統擴展很是容易。若是隨着大家系統業務的發展,當前的系統又支撐不住了,那麼給這個集羣再增長節點就好了。可是,當你的業務發展到必定程度的時候,你會發現一個問題——不管怎麼增長節點,貌似整個集羣性能的提高效果並不明顯了。這時候,你就須要使用微服務結構了。

微服務結構

先來對前面的知識點作個總結。
從單機結構到集羣結構,你的代碼基本無須要做任何修改,你要作的僅僅是多部署幾臺服務器,沒太服務器上運行相同的代碼就好了。可是,當你要從集羣結構演進到微服務結構的時候,以前的那套代碼就須要發生較大的改動了。因此對於新系統咱們建議,系統設計之初就採用微服務架構,這樣後期運維的成本更低。但若是一套老系統須要升級成微服務結構的話,那就得對代碼大動干戈了。因此,對於老系統而言,到底是繼續保持集羣模式,仍是升級成微服務架構,這須要大家的架構師深思熟慮、權衡投入產出比。

OK,下面開始介紹所謂的微服務。
微服務就是將一個完整的系統,按照業務功能,拆分紅一個個獨立的子系統,在微服務結構中,每一個子系統就被稱爲「服務」。這些子系統可以獨立運行在web容器中,它們之間經過RPC方式通訊。

舉個例子,假設須要開發一個在線商城。按照微服務的思想,咱們須要按照功能模塊拆分紅多個獨立的服務,如:用戶服務、產品服務、訂單服務、後臺管理服務、數據分析服務等等。這一個個服務都是一個個獨立的項目,能夠獨立運行。若是服務之間有依賴關係,那麼經過RPC方式調用。

這樣的好處有不少:

  1. 系統之間的耦合度大大下降,能夠獨立開發、獨立部署、獨立測試,系統與系統之間的邊界很是明確,排錯也變得至關容易,開發效率大大提高。
  2. 系統之間的耦合度下降,從而系統更易於擴展。咱們能夠針對性地擴展某些服務。假設這個商城要搞一次大促,下單量可能會大大提高,所以咱們能夠針對性地提高訂單系統、產品系統的節點數量,而對於後臺管理系統、數據分析系統而言,節點數量維持原有水平便可。
  3. 服務的複用性更高。好比,當咱們將用戶系統做爲單獨的服務後,該公司全部的產品均可以使用該系統做爲用戶系統,無需重複開發。

那麼問題來了,當採用微服務結構後,一個完整的系統可能有不少獨立的子系統組成,當業務量漸漸發展起來以後,而這些子系統之間的關係將錯綜複雜,並且爲了可以針對性地增長某些服務的處理能力,某些服務的背後多是一個集羣模式,由多個節點構成,這無疑大大增長了運維的難度。微服務的想法好是好,但開發、運維的複雜度實在是過高。爲了解決這些問題,阿里巴巴的Dubbo就橫空出世了。

知識點2:Dubbo

Dubbo是一套微服務系統的協調者,在它這套體系中,一共有三種角色,分別是:服務提供者(下面簡稱提供者)、服務消費者(下面簡稱消費者)、註冊中心。

你在使用的時候須要將Dubbo的jar包引入到你的項目中,也就是每一個服務都要引入Dubbo的jar包。而後當這些服務初始化的時候,Dubbo就會將當前系統須要發佈的服務、以及當前系統的IP和端口號發送給註冊中心,註冊中心便會將其記錄下來。這就是服務發佈的過程。與此同時,也是在系統初始化的時候,Dubbo還會掃描一下當前系統所須要引用的服務,而後向註冊中心請求這些服務所在的IP和端口號。接下來系統就能夠正常運行了。當系統A須要調用系統B的服務的時候,A就會與B創建起一條RPC信道,而後再調用B系統上相應的服務。

這,就是Dubbo的做用。

知識點3:容器化部署

當咱們使用了微服務架構後,咱們將一個本來完整的系統,按照業務邏輯拆分紅一個個可獨立運行的子系統。爲了下降系統間的耦合度,咱們但願這些子系統可以運行在獨立的環境中,這些環境之間可以相互隔離。

在Docker出現以前,若使用虛擬機來實現運行環境的相互隔離的話成本較高,虛擬機會消耗較多的計算機硬件/軟件資源。Docker不只可以實現運行環境的隔離,並且能極大程度的節約計算機資源,它成爲一種輕量級的「虛擬機」。

知識點4:自動化構建

當咱們使用微服務架構後,隨着業務的逐漸發展,系統之間的依賴關係會日益複雜,並且各個模塊的構建順序都有所講究。對於一個小型系統來講,也許只有幾個模塊,那麼你每次採用人肉構建的方式也許並不感受麻煩。但隨着系統業務的發展,你的系統之間的依賴關係日益複雜,子系統也逐漸增多,每次構建一下你都要很是當心謹慎,稍有不慎整個服務都沒法正常啓動。並且這些構建的工做很low,但卻須要消耗大量的精力,這無疑下降了開發的效率。不過不要緊,Jenkins就是來幫助你解決這個問題的。

咱們只需在Jenkins中配置好代碼倉庫、各個模塊的構建順序和構建命令,在之後的構建中,只須要點擊「當即構建」按鈕,Jenkins就會自動到你的代碼倉庫中拉取最新的代碼,而後根據你事先配置的構建命令進行構建,最後發佈到指定的容器中運行。你也可讓Jenkins定時檢查代碼倉庫版本的變化,一旦發現變更就自動地開始構建過程,而且讓Jenkins在構建成功後給你發一封郵件。這樣你連「當即構建」的按鈕也不須要按,就能全自動地完成這一切構建過程。


實戰動手篇

1. 學習目標

接下來我會帶着你們,以一個在線商城爲例,搭建一套可以自動化部署的微服務框架。這個框架能作以下幾件事情:

  1. 基於SpringBoot快速開發。咱們將選擇目前熱度很高的SpringBoot,最大限度地下降配置複雜度,把大量的精力投入到咱們的業務開發中來。
  2. 基於Dubbo的微服務化。咱們會使用阿里巴巴的開源框架Dubbo,將咱們的系統拆分紅多個獨立的微服務,而後用Dubbo來管理全部服務的發佈和引用。有了Dubbo以後,調用遠程服務就像調用一個本地函數同樣簡單,Dubbo會幫咱們完成遠程調用背後所須要的一切。
  3. 基於Docker的容器化部署。因爲使用了微服務架構後,咱們的系統將會由不少子系統構成。爲了達到多個系統之間環境隔離的目的,咱們能夠將它們部署在多臺服務器上,可這樣的成本會比較高,並且每臺服務器的性能可能都沒有充分利用起來。因此咱們很天然地想到了虛擬機,在同一臺服務器上運行多個虛擬機,從而實現環境的隔離,每一個虛擬機上運行獨立的服務。然而虛擬機的隔離成本依舊很高,由於它須要佔用服務器較多的硬件資源和軟件資源。因此,在微服務結構下,要實現服務環境的隔離,Docker是最佳選擇。它比虛擬機更加輕量級,佔用資源較少,並且可以實現快速部署。
  4. 基於Jenkins的自動化構建。當咱們採用了微服務架構後,咱們會發現這樣一個問題。整個系統由許許多多的服務構成,這些服務都須要運行在單獨的容器中,那麼每次發佈的複雜度將很是高。首先你要搞清楚這些服務之間的依賴關係、啓動的前後順序,而後再將多個子系統挨個編譯、打包、發佈。這些操做技術難度低,卻又容易出錯。那麼有什麼工具可以幫助咱們解決這些問題呢?答案就是——Jenkins。
    它是一款自動化構建的工具,簡單的來講,就是咱們只須要在它的界面上按一個按鈕,就能夠實現上述一系列複雜的過程。

2. 項目背景介紹

本文我以一個你們都很是熟悉的在線商城做爲例子,一步步教你們如何搭建微服務框架,它有以下功能:

  • 產品管理:產品的增刪改查。
  • 訂單管理:訂單的增刪改查、購物車功能。
  • 用戶管理:用戶的登陸、註冊、權限管理、收貨地址等等。
  • 數據分析:提供對本系統數據分析的功能。
注意:本文的IDE使用的是intelliJ IDEA,推薦你們也用這個,用了都說好,用了你就會愛上它。

3. 建立項目的組織結構

在動手以前,我先來講一說這一步的目標:

  • 建立一個Maven Project,命名爲「Gaoxi」
    這個Project由多個Module構成,每一個Module對應着「微服務」的一個子系統,可獨立運行,是一個獨立的項目。

這也是目前主流的項目組織形式,即多模塊項目。

  • 在Gaoxi這個項目下建立各個子模塊,每一個自模塊都是一個獨立的SpringBoot項目:

    • Gaoxi-User
      用戶服務
    • Gaoxi-Order
      訂單服務
    • Gaoxi-Product
      產品服務
    • Gaoxi-Analysis
      數據分析服務
    • Gaoxi-Controller
      本系統的控制層,和以往三層結構中的Controller層的做用同樣,都是用做請求調度,只不過在微服務架構中,咱們將它抽象成一個單獨的系統,能夠獨立運行。
    • Gaoxi-Common-Service-Facade
      它處於本系統的最底層,被全部模塊依賴,一些公用的類庫都放在這裏。
    • Gaoxi-Redis
      咱們將Redis封裝成一個單獨的服務,運行在獨立的容器中,當哪個模塊須要使用Redis的時候,僅須要引入該服務便可,就免去了各類繁瑣的、重複的配置。而這些配置均在Gaoxi-Redis系統中完成了。

title

下面開始動手。

3.1 建立Project

  • New一個Project
    建立Project
  • 選擇Spring Initializr
    title
  • 設置groupId、artifactId、version

    <groupId>com.gaoxi</groupId>
    <artifactId>gaoxi</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  • Project建立完畢!接下來在Project下面建立Module

3.2 建立Module

  • 在Project上New Module
    title
  • 和剛纔同樣,選擇Spring Initializr,設置groupId、artifactId、version
  • 依次建立好全部的Module,以下圖所示:
    title

3.3 構建模塊的依賴關係

目前爲止,模塊之間沒有任何聯繫,下面咱們要經過pom文件來指定它們之間的依賴關係,依賴關係以下圖所示:
title
Gaoxi-User、Gaoxi-Analysis、Gaoxi-Product、Gaoxi-Order這四個系統至關於以往三層結構的Service層,提供系統的業務邏輯,只不過在微服務結構中,Service層的各個模塊都被抽象成一個個單獨的子系統,它們提供RPC接口供上面的Gaoxi-Controller調用。它們之間的調用由Dubbo來完成,因此它們的pom文件中並不須要做任何配置。而這些模塊和Gaoxi-Common-Service-Facade之間是本地調用,所以須要將Gaoxi-Common-Service-Facade打成jar包,並讓這些模塊依賴這個jar,所以就須要在全部模塊的pom中配置和Gaoxi-Common-Service-Facade的依賴關係。

此外,爲了簡化各個模塊的配置,咱們將全部模塊的通用依賴放在Project的pom文件中,而後讓全部模塊做爲Project的子模塊。這樣子模塊就能夠從父模塊中繼承全部的依賴,而不須要本身再配置了。

下面開始動手:

  • 首先將Common-Service-Facade的打包方式設成jar
    當打包這個模塊的時候,Maven會將它打包成jar,並安裝在本地倉庫中。這樣其餘模塊打包的時候就能夠引用這個jar。

    <groupId>com.gaoxi</groupId>
    <artifactId>gaoxi-common-service-facade</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>
  • 將其餘模塊的打包方式設爲war
    除了Gaoxi-Common-Service-Facade外,其餘模塊都是一個個可獨立運行的子系統,須要在web容器中運行,因此咱們須要將這些模塊的打包方式設成war

    <groupId>com.gaoxi</groupId>
    <artifactId>gaoxi-user</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
  • 在總pom中指定子模塊
    modules標籤指定了當前模塊的子模塊是誰,可是僅在父模塊的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>
到此爲止,模塊的依賴關係配置完畢!但要注意模塊打包的順序。因爲全部模塊都依賴於Gaoxi-Common-Servie-Facade模塊,所以在構建模塊時,首先須要編譯、打包、安裝Gaoxi-Common-Servie-Facade,將它打包進本地倉庫中,這樣上層模塊才能引用到。當該模塊安裝完畢後,再構建上層模塊。不然在構建上層模塊的時候會出現找不到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>

當父模塊的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>

當項目的結構搭建完成以後,接下來你須要配置Docker環境,並將這些項目打包進容器中,驗證下是否能正常啓動。

4. 建立Docker容器

4.1 安裝Docker

在使用Docker以前,你固然先要安裝Docker,安裝過程較爲簡單,基本上就是傻瓜式操做,這裏就不做過多介紹了,你能夠在Docker的官網下載相應系統的安裝包。
https://www.docker.com/

4.2 獲取Tomcat鏡像

在微服務架構中,一個完整的系統被拆分紅了多個被稱爲「微服務」的子系統,這些子系統能夠獨立運行在Web容器中。因此咱們須要爲這些系統提供運行的Web容器,這裏咱們選擇你們較爲熟悉的Tomcat。

咱們知道,Tomcat依賴於Java環境,安裝Tomcat以前要進行一系列環境的配置:安裝Java、配置環境變量、安裝Tomcat等等。這些操做仍是有些繁瑣的。不過不要緊,當使用了Docker以後,這些過程均可以垂手可得地完成。

咱們只需從Docker Hub上找到Tomcat的鏡像資源,而後從上面拉取下來就可使用。你可使用Tomcat官方的鏡像,也可使用我發佈在Docker Hub上的Tomcat鏡像。

注意點:推薦使用個人Tomcat鏡像資源chaimm/tomcat,由於這個鏡像中除了配置Tomcat的安裝環境之外,還有一些本項目中要用到的Jenkins相關的配置。

採用以下命令從Docker Hub上拉取鏡像:

docker pull chaimm/tomcat:1.1

簡單解釋下,docker pull是從從Docker Hub上拉取鏡像的命令,後面的chaimm/tomcat是鏡像的名稱,:1.1是鏡像的版本號。目前這個鏡像的最新版本號是1.1,推薦你們拉取這個。

4.3 建立Tomcat容器

這裏再簡單介紹下「鏡像」和「容器」的關係。
「鏡像」就比如是面向對象中的「類」,「容器」就比如「類」建立的「對象」。在面向對象中,「類」定義了各類屬性,「類」能夠實例化出多個「對象」;而在Docker中,「鏡像」定義了各類配置信息,它能夠實例化出多個「容器」。「容器」就是一臺能夠運行的「虛擬機」。

接下來咱們須要爲全部的微服務建立各自的容器:

  • gaoxi-user
  • gaoxi-product
  • gaoxi-order
  • gaoxi-analysis
  • gaoxi-controller
  • gaoxi-redis

以建立gaoxi-user容器爲例,採用以下命令建立容器:

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端口映射到宿主機的8082端口上

  • -v:指定容器數據卷的映射
    xxx:yyy 表示將容器yyy目錄映射到宿主機的xxx目錄上,從而訪問宿主機的xxx目錄就至關於訪問容器的yyy目錄。
  • chaimm/tomcat:1.1:表示容器所對應的鏡像。

這條命令執行成功後,你就能夠經過你的IP:8082 訪問到gaoxi-user-1容器的tomcat了。若是你看到了那隻眼熟了貓,那就說明容器啓動成功了!
title

接下來,你須要按照上面的方法,給剩下幾個系統建立好Tomcat容器。

注意點:這裏要注意的是,你須要給這些Tomcat容器指定不一樣的端口號,防止端口號衝突。固然,在實際開發中,你並不須要將容器的8080端口映射到宿主機上,這裏僅僅是爲了驗證容器是否啓動成功才這麼作的。

5. 整合Dubbo

5.1 建立zookeeper容器

Dubbo一共定義了三種角色,分別是:服務提供者、服務消費者、註冊中心。註冊中心是服務提供者和服務消費者的橋樑,服務消費者會在初始化的時候將本身的IP和端口號發送給註冊中心,而服務消費者經過註冊中心知道服務提供者的IP和端口號。

在Dubbo中,註冊中心有多種選擇,Dubbo最爲推薦的即爲ZooKeeper,本文采用ZooKeepeer做爲Dubbo的註冊中心。

建立ZooKeeper容器也較爲簡單,你們能夠直接使用我建立的ZooKeeper鏡像,經過以下命令便可下載鏡像:

docker pull chaimm/zookeeper-dubbo:1.0

該鏡像中不只運行了一個zookeeper,還運行了一個擁有dubbo-admin項目的tomcat。dubbo-admin是Dubbo的一個可視化管理工具,能夠查看服務的發佈和引用的狀況。

使用以下命令啓動容器:

docker run --name zookeeper-debug -p 2182:2181 -p 10000:8080 chaimm/zookeeper-dubbo:1.0
  • -p 2182:2181:將容器的2181端口映射到宿主機的2182端口上,該端口是ZooKeeper的端口號。
  • -p 10000:8080:將容器的8080端口映射到宿主機的10000端口上,該端口是Dubbo-Admin所在Tomcat的端口號。

啓動成功後,你就能夠經過你的IP:10000/dubbo-admin-2.8.4/訪問到Dubbo-Admin,以下圖所示:
title

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 發佈服務

假設,咱們須要將Gaoxi-User項目中的UserService發佈成一項RPC服務,供其餘系統遠程調用,那麼咱們究竟該如何藉助Dubbo來實現這一功能呢?
  • 在Gaoxi-Common-Service-Facade中定義UserService的接口
    因爲服務的發佈和引用都依賴於接口,但服務的發佈方和引用方在微服務架構中每每不在同一個系統中,因此須要將須要發佈和引用的接口放在公共類庫中,從而雙方都可以引用。接口以下所示:

    public interface UserService {
    
        public UserEntity login(LoginReq loginReq);
    }
  • 在Gaoxi-User中定義接口的實現
    在實現類上須要加上Dubbo的@Service註解,從而Dubbo會在項目啓動的時候掃描到該註解,將它發佈成一項RPC服務。

    @Service(version = "1.0.0")
    public class UserServiceImpl implements UserService {
    
        @Override
        public UserEntity login(LoginReq loginReq) {
            // 具體的實現代碼
        }
    }
  • 在Gaoxi-User的application.properties中配置服務提供者的信息

    spring.dubbo.application.name=user-provider # 本服務的名稱
    spring.dubbo.registry.address=zookeeper://IP:2182 # ZooKeeper所在服務器的IP和端口號
    spring.dubbo.protocol.name=dubbo # RPC通訊所採用的協議
    spring.dubbo.protocol.port=20883 # 本服務對外暴露的端口號
    spring.dubbo.scan=com.gaoxi.user.service # 服務實現類所在的路徑
    按照上面配置完成後,當Gaoxi-User系統初始化的時候,就會掃描spring.dubbo.scan所指定的路徑下的@Service註解,該註解標識了須要發佈成RPC服務的類。Dubbo會將這些類的接口信息+本服務器的IP+spring.dubbo.protocol.port所指定的端口號發送給Zookeeper,Zookeeper會將這些信息存儲起來。

這就是服務發佈的過程,下面來看如何引用一項RPC服務。

5.4 引用服務

假設,Gaoxi-Controller須要調用Gaoxi-User 提供的登陸功能,此時它就須要引用UserService這項遠程服務。下面來介紹服務引用的方法。
  • 聲明須要引用的服務
    引用服務很是簡單,你只須要在引用的類中聲明一項服務,而後用@Reference標識,以下所示:

    @RestController
    public class UserControllerImpl implements UserController {
    
        @Reference(version = "1.0.0")
        private UserService userService;
        
        @Override
        public Result login(LoginReq loginReq, HttpServletResponse httpRsp) {
    
            // 登陸鑑權
            UserEntity userEntity = userService.login(loginReq);
        }
    }
  • 在Gaoxi-Controller的application.properties中配置服務消費者的信息

    spring.dubbo.application.name=controller-consumer # 本服務的名稱
    spring.dubbo.registry.address=zookeeper://IP:2182 # zookeeper所在服務器的IP和端口號
    spring.dubbo.scan=com.gaoxi # 引用服務的路徑
上述操做完成後,當Gaoxi-Controller初始化的時候,Dubbo就會掃描spring.dubbo.scan所指定的路徑,並找到全部被@Reference修飾的成員變量;而後向Zookeeper請求該服務所在的IP和端口號。當調用userService.login()的時候,Dubbo就會向Gaoxi-User發起請求,完成調用的過程。這個調用過程是一次RPC調用,但做爲程序猿來講,這和調用一個本地函數沒有任何區別,遠程調用的一切都由Dubbo來幫你完成。這就是Dubbo的做用。

6. 自動化構建

Jenkins是一個自動化構建工具,它能夠幫助咱們擺脫繁瑣的部署過程,咱們只須要在一開始配置好構建策略,之後部署只須要一鍵完成。

6.1 建立Jenkins容器

Jenkins採用Java開發,也須要Java環境,但咱們使用Docker後,一切都採用容器化部署,Jenkins也不例外。

  • 拉取鏡像
    這裏咱們使用Jenkins官方提供的鏡像,你們只需執行以下命令拉取便可:

    docker pull docker.io/jenkins/jenkins
  • 啓動容器
    因爲Jenkins運行在Tomcat容器中,所以咱們將容器的8080端口映射到宿主機的10080端口上:

    docker run --name jenkins -p 10080:8080 docker.io/jenkins/jenkins
  • 初始化Jenkins
    而後你須要訪問IP:10080Jenkins會帶着你進行一系列的初始化設置,你只要跟着它一步步走就好了,比較傻瓜式。

6.2 在Jenkins中建立項目

接下來咱們要作的是,在Jenkins中爲每個服務建立一個項目,每一個項目中定義了構建的具體流程。因爲咱們將整個項目分紅了6個微服務,因此咱們須要在Jenkins中分別爲這6個服務建立項目。那句開始吧~
  • 點擊頁面左側的「新建」按鈕:
    title
  • 輸入項目名稱gaoxi-user,選擇「構建一個Maven項目」,而後點擊「OK」:
    title
  • 配置Git倉庫
    選擇Git,而後輸入本項目Git倉庫的URL,並在Credentials中輸入Git的用戶名和密碼,以下圖所示:
    title
  • 構建觸發器
    選擇第一項,以下圖所示:
    title
  • Pre Step
    Pre Step會在正式構建前執行,因爲全部項目都依賴於Gaoxi-Common-Service—Facade,所以在項目構建前,須要將它安裝到本地倉庫,而後才能被當前項目正確依賴。
    所以,在Pre Step中填寫以下信息:
    title
  • Build
    而後就是正式構建的過程,填寫以下信息便可:
    title
OK,Gaoxi-User的構建過程就配置完成了。當咱們點擊「當即構建」按鈕時,Jenkins首先會從咱們指定的Git倉庫中拉取代碼,而後執行Pre Step中的Maven命令,將Gaoxi-Common-Serivce-Facade打包安裝到本地倉庫。而後執行Build過程,將Gaoxi-User進行編譯打包。
但此時Gaoxi-User仍然只是一個本地war包,並無部署到Tomcat容器中,而咱們採用了容器化部署後,Jenkins服務和Gaoxi-User服務並不在同一個Docker容器中,那麼究竟該如何才能將Jenkins本地編譯好的war包發送到Gaoxi-User容器中呢?這就須要使用Jenkins的一個插件——Deploy Plugin。

6.3 遠程部署

  • 下載插件
    首先你須要下載Deploy Plugin,下載地址以下:

https://wiki.jenkins.io/displ...

  • 安裝插件
    在系統管理–>插件管理–>高級上傳deploy.hpi進行安裝。
  • 在父項目的pom文件中增長遠程部署插件:

    <plugin>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>cargo-maven2-plugin</artifactId>
        <version>1.6.5</version>
        <configuration>
            <container>
                <!-- 指明使用的tomcat服務器版本 -->
                <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設置用戶名和密碼
    修改gaoxi-user容器中tomcat的tomcat-users.xml文件,增長tomcat的manager用戶
    title
注意:若是你使用了chaimm/tomcat鏡像,那麼其中Tomcat配置都已經完成,默認用戶名:admin、默認密碼:jishimen2019。強烈建議修改用戶名和密碼。
  • 修改Jenkins中gaoxi-user的配置
    在「構建後操做」中增長以下配置:
    title

    - WAR/EAR files:表示你須要發佈的war包
    - Containers:配置目標Tomcat的用戶名和密碼

7. Maven的profile功能

在實際開發中,咱們的系統每每有多套環境構成,如:開發環境、測試環境、預發環境、生產環境。而不一樣環境的配置各不相同。若是咱們只有一套配置,那麼當系統從一個環境遷移到另外一個環境的時候,就須要經過修改代碼來更換配置,這樣無疑增長了工做的複雜度,並且易於出錯。但好在Maven提供了profile功能,能幫助咱們解決這一個問題。
  • 父項目的pom中添加profile元素
    首先,咱們須要在總pom的中添加多套環境的信息,以下所示:

    <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標識了不一樣環境下須要打包哪些配置文件。

    <resources>
        <resource>
            <!-- 標識配置文件所在的目錄 -->
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <!-- 構建時將這些配置文件全都排除掉 -->
            <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>
            <!-- 標識構建時所須要的配置文件 -->
            <includes>
                <include>application.properties</include>
                <!-- ${profileActive}這個值會在maven構建時傳入 -->
                <include>application-${profileActive}.properties</include>
            </includes>
        </resource>
    </resources>
  • 父項目的pom中添加插件maven-resources-plugin
    該插件用來在Maven構建時參數替換

    <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.0.2</version>
        <configuration>
            <delimiters>
                <delimiter>@</delimiter>
            </delimiters>
            <useDefaultDelimiters>false</useDefaultDelimiters>
        </configuration>
    </plugin>
  • 在子項目中建立配置
    分別爲dev環境、test環境、prod環境建立三套配置,application.proerpties中存放公用的配置。
    title

    • 在application.properties中添加spring.profiles.active=@profileActive@
    spring.profiles.active=@profileActive@
  • 修改Jenkins的配置
    在全部Jenkins中全部Maven命令的末尾添加 -P test,在打包的時候-P後面的參數將會做爲@profileActive@的值傳入系統中,從而根據該值打包相應的application-{profileActive}.properties文件。

8. 開發流程

到此爲止,全部準備工做都已經完成,接下來就能夠進入代碼開發階段。下面我以一個例子,帶着你們感覺下有了這套微服務框架後,咱們的開發流程究竟有了哪些改變?下面以開發一個用戶登陸功能爲例,介紹下使用本框架以後開發的流程。

8.1 開發目標

  • 在Gaoxi-User系統中實現登陸的業務邏輯,併發布成RPC服務
  • 在Gaoxi-Controller中遠程調用登陸服務,並向前端提供登陸的REST接口

title

8.2 開發登陸服務

首先須要在Gaoxi-Common-Service-Facade中建立UserService接口,並在其中聲明登陸的抽象函數。

public interface UserService {

    public UserEntity login(LoginReq loginReq);
}
PS:爲何要將UserService放在Gaoxi-Common-Service-Facade中?
在這個項目中,Gaoxi-User是UserService服務的提供方,Gaoxi-Controller是UserService服務的引用方。因爲兩者並不在同一個系統中,因此必需要藉助於Dubbo來實現遠程方法調用。而Dubbo發佈服務和引用服務的時候,都是根據服務的接口標識服務的,即服務引用方和發佈方都須要使用服務的接口,所以須要將服務的接口放在全部項目共同依賴的基礎模塊——Gaoxi-Common-Service-Facade中。

而後在Gaoxi-User中開發UserService的實現——UserServiceImpl。
UserServiceImpl上必需要加上Dubbo的@Service註解,從而告訴Dubbo,在本項目初始化的時候須要將這個類發佈成一項服務,供其餘系統調用。

@Service(version = "1.0.0")
@org.springframework.stereotype.Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    @Override
    public UserEntity login(LoginReq loginReq) {

        // 校驗參數
        checkParam(loginReq);

        // 建立用戶查詢請求
        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 引用登陸服務

當UserService開發完畢後,接下來Gaoxi-Controller須要引用該服務,並向前端提供一個登陸的REST接口。
若要使用userService中的函數,僅須要在userService上添加@Reference註解,而後就像調用本地函數同樣使用userService便可。Dubbo會幫你找到UserService服務所在的IP和端口號,併發送調用請求。但這一切對於程序猿來講是徹底透明的。

@RestController
public class UserControllerImpl implements UserController {
    @Reference(version = "1.0.0")
    private UserService userService;
    
    /**
     * 登陸
     * @param loginReq 登陸請求參數
     * @param httpRsp HTTP響應
     * @return 登陸是否成功
     */
    @GetMapping("/login")
    @Override
    public Result login(LoginReq loginReq, HttpServletResponse httpRsp) {

        // 登陸鑑權
        UserEntity userEntity = userService.login(loginReq);

        // 登陸成功
        doLoginSuccess(userEntity, httpRsp);
        return Result.newSuccessResult();
    }
}

8.4 自動構建服務

上面的代碼完成後,接下來你須要將代碼提交至你的Git倉庫。接下來就是自動化部署的過程了。

你須要進入Jenkins,因爲剛纔修改了Gaoxi-User和Gaoxi-Controller的代碼,所以你須要分別構建這兩個項目。
接下來Jenkins會自動從你的Git倉庫中拉取最新的代碼,而後依次執行Pre Step、Build、構建後操做的過程。因爲咱們在Pre Step中設置了編譯Gaoxi-Common-Service-Facade,所以Jenkins首先會將其安裝到本地倉庫;而後再執行Build過程,構建Gaoxi-User,並將其打包成war包。最後將執行「構建後操做」,將war包發佈到相應的tomcat容器中。
至此,整個發佈流程完畢!

8.5 查看服務的狀態

當Jenkins構建完成後,咱們能夠登陸Dubbo-Admin查看服務發佈和引用的狀態。

當咱們搜索UserService服務後,能夠看到,該服務的提供者已經成功發佈了服務:
title

點擊「消費者」咱們能夠看到,該服務已經被controller-consumer成功訂閱:
title

9. 總結

總結一下,這套框架有以下優點:

  1. 微服務架構
    咱們藉助於SpringBoot和Dubbo實現了微服務架構。微服務架構的理念就是將一個本來龐大、複雜的系統,按照業務功能拆分紅一個個具備獨立功能、能夠獨立運行的子系統,系統之間如有依賴,則經過RPC接口通訊。從而最大限度地下降了系統之間的耦合度,從而更加易於擴展、更加易於維護。
  2. 容器化部署
    咱們藉助於Docker實現了容器化部署。容器可以幫助咱們屏蔽不一樣環境下的配置問題,使得咱們只須要有一個Dockerfile文件,就能夠到處運行。和虛擬機同樣,Docker也擁有環境隔離的能力,但比虛擬機更加輕量級,因爲每一個容器僅僅是一條進程,所以它能夠達到秒級的啓動速度。
  3. 自動化構建
    咱們藉助於Jenkins實現了全部項目的自動化構建與部署。咱們只須要點擊「當即構建」這個按鈕,Jenkins就能夠幫助咱們梳理好錯綜複雜的項目依賴關係,準確無誤地完成構建,並將war包發送到相應的web容器中。在啓動的過程當中,Dubbo會掃描當前項目所須要發佈和引用的服務,將所須要發佈的服務發佈到ZooKeeper上,並向ZooKeeper訂閱所需的服務。
    有了Jenkins以後,這一切都是自動化完成。也許你並無太強烈地感覺到Jenkins所帶來的便利。可是你想想,對於一個具備錯綜複雜的依賴關係的微服務系統而言,若是每一個服務的構建都須要你手動完成的話,你很快就會崩潰,你大把的時間將會投入在無聊但又容易出錯的服務構建上。而Jenkins的出現能讓這一切自動化完成。

image

相關文章
相關標籤/搜索