1. 程式人生 > >零基礎秒懂:手把手教你搭建一套微服務框架!

零基礎秒懂:手把手教你搭建一套微服務框架!

這套微服務框架能幹啥?

這套系統搭建完之後,可以實現:

  • 微服務架構,你的整個應用程式將會被拆分成一個個功能獨立的子系統,獨立執行,系統與系統之間通過 RPC 介面通訊。

    這樣系統之間的耦合度大大降低,你的系統將非常容易擴充套件,團隊協作效率提升了 N 個檔次。

    這種架構通過眼下流行的 Spring Boot 和阿里巴巴吊 Dubbo 框架來實現。

  • 容器化部署,你的各個微服務將採用目前處於浪潮之巔的 Docker 來實現容器化部署,避免一切因環境引起的各種問題,讓你們團隊的全部精力集中在業務開發上。

  • 自動化構建,專案被微服務化後,各個服務之間的關係錯綜複雜,打包構建的工作量相當可怕。不過沒關係,本文將藉助 Jenkins,幫助你一鍵自動化部署,從此你便告別了加班。

知識點掃盲篇

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

知識點 1:微服務

微服務一詞近幾年相當火,成為程式猿裝逼熱門詞彙,你不對它有所瞭解如何在程式猿裝逼圈子裡混?下面我用最為通俗易懂的語言介紹它。要講清楚微服務,我先要從一個系統架構的演進過程講起。

單機結構

我想大家最最最熟悉的就是單機結構,一個系統業務量很小的時候所有的程式碼都放在一個專案中,然後這個專案部署在一臺伺服器上就好了。整個專案所有的服務都由這臺伺服器提供。這就是單機結構。

那麼,單機結構有啥缺點呢?單機的處理能力畢竟有限,當你的業務增長到一定程度的時候,單機的硬體資源將無法滿足你的業務需求。此時便出現了叢集模式。

叢集結構

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

單機處理到達瓶頸的時候,你就把單機複製幾份,這樣就構成了一個“叢集”。叢集中每臺伺服器就叫做這個叢集的一個“節點”,所有節點構成了一個叢集。

每個節點都提供相同的服務,那麼這樣系統的處理能力就相當於提升了好幾倍(有幾個節點就相當於提升了這麼多倍)。

但問題是使用者的請求究竟由哪個節點來處理呢?最好能夠讓此時此刻負載較小的節點來處理,這樣使得每個節點的壓力都比較平均。

要實現這個功能,就需要在所有節點之前增加一個“排程者”的角色,使用者的所有請求都先交給它,然後它根據當前所有節點的負載情況,決定將這個請求交給哪個節點處理。這個“排程者”有個牛逼的名字——負載均衡伺服器。

叢集結構的好處就是系統擴充套件非常容易。隨著你們系統業務的發展,當前的系統又支撐不住了,那麼給這個叢集再增加節點就行了。

但是,當你的業務發展到一定程度的時候,你會發現一個問題——無論怎麼增加節點,貌似整個叢集效能的提升效果並不明顯了。這時候,你就需要使用微服務結構了。

微服務結構

先來對前面的知識點做個總結。從單機結構到叢集結構,你的程式碼基本無需要作任何修改,你要做的僅僅是多部署幾臺伺服器,沒臺伺服器上執行相同的程式碼就行了。

但是,當你要從叢集結構演進到微服務結構的時候,之前的那套程式碼就需要發生較大的改動了。

所以對於新系統我們建議,系統設計之初就採用微服務架構,這樣後期運維的成本更低。

但如果一套老系統需要升級成微服務結構的話,那就得對程式碼大動干戈了。所以,對於老系統而言,究竟是繼續保持叢集模式,還是升級成微服務架構,這需要你們的架構師深思熟慮、權衡投入產出比。

下面開始介紹所謂的微服務。 

微服務就是將一個完整的系統,按照業務功能,拆分成一個個獨立的子系統,在微服務結構中,每個子系統就被稱為“服務”。這些子系統能夠獨立執行在 Web 容器中,它們之間通過 RPC 方式通訊。

舉個例子,假設需要開發一個線上商城。按照微服務的思想,我們需要按照功能模組拆分成多個獨立的服務,如:使用者服務、產品服務、訂單服務、後臺管理服務、資料分析服務等等。

這一個個服務都是一個個獨立的專案,可以獨立執行。如果服務之間有依賴關係,那麼通過 RPC 方式呼叫。

這樣的好處有很多:

  • 系統之間的耦合度大大降低,可以獨立開發、獨立部署、獨立測試,系統與系統之間的邊界非常明確,排錯也變得相當容易,開發效率大大提升。

  • 系統之間的耦合度降低,從而系統更易於擴充套件。我們可以針對性地擴充套件某些服務。假設這個商城要搞一次大促,下單量可能會大大提升,因此我們可以針對性地提升訂單系統、產品系統的節點數量,而對於後臺管理系統、資料分析系統而言,節點數量維持原有水平即可。

  • 服務的複用性更高。比如,當我們將使用者系統作為單獨的服務後,該公司所有的產品都可以使用該系統作為使用者系統,無需重複開發。

那麼問題來了,當採用微服務結構後,一個完整的系統可能由很多獨立的子系統組成,當業務量漸漸發展起來之後,這些子系統之間的關係將錯綜複雜。

而且為了能夠針對性地增加某些服務的處理能力,某些服務的背後可能是一個叢集模式,由多個節點構成,這無疑大大增加了運維的難度。

微服務的想法好是好,但開發、運維的複雜度實在是太高。為了解決這些問題,阿里巴巴的 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 在構建成功後給你發一封郵件。這樣你連“立即構建”的按鈕也不需要按,就能全自動地完成這一切構建過程。

實戰動手篇

學習目標

接下來我會帶著大家,以一個線上商城為例,搭建一套能夠自動化部署的微服務框架。

這個框架能做如下幾件事情:

  • 基於 Spring Boot 快速開發,我們將選擇目前熱度很高的 Spring Boot,最大限度地降低配置複雜度,把大量的精力投入到我們的業務開發中來。

  • 基於 Dubbo 的微服務化,我們會使用阿里巴巴的開源框架 Dubbo,將我們的系統拆分成多個獨立的微服務,然後用 Dubbo 來管理所有服務的釋出和引用。

    有了 Dubbo 之後,呼叫遠端服務就像呼叫一個本地函式一樣簡單,Dubbo 會幫我們完成遠端呼叫背後所需要的一切。

  • 基於 Docker 的容器化部署,由於使用了微服務架構後,我們的系統將會由很多子系統構成。

    為了達到多個系統之間環境隔離的目的,我們可以將它們部署在多臺伺服器上,可這樣的成本會比較高,而且每臺伺服器的效能可能都沒有充分利用起來。

    所以我們很自然地想到了虛擬機器,在同一臺伺服器上執行多個虛擬機器,從而實現環境的隔離,每個虛擬機器上執行獨立的服務。然而虛擬機器的隔離成本依舊很高,因為它需要佔用伺服器較多的硬體資源和軟體資源。

    所以,在微服務結構下,要實現服務環境的隔離,Docker 是最佳選擇。它比虛擬機器更加輕量級,佔用資源較少,而且能夠實現快速部署。

  • 基於 Jenkins 的自動化構建,當我們採用了微服務架構後,我們會發現這樣一個問題。整個系統由許許多多的服務構成,這些服務都需要執行在單獨的容器中,那麼每次釋出的複雜度將非常高。

    首先你要搞清楚這些服務之間的依賴關係、啟動的先後順序,然後再將多個子系統挨個編譯、打包、釋出。這些操作技術難度低,卻又容易出錯。

    那麼有什麼工具能夠幫助我們解決這些問題呢?答案就是——Jenkins。 它是一款自動化構建的工具,簡單的來說,就是我們只需要在它的介面上按一個按鈕,就可以實現上述一系列複雜的過程。

專案背景介紹

本文我以一個線上商城作為例子,一步步教大家如何搭建微服務框架,它有如下功能:

  • 產品管理,產品的增刪改查。

  • 訂單管理,訂單的增刪改查、購物車功能。

  • 使用者管理,使用者的登入、註冊、許可權管理、收貨地址等等。

  • 資料分析,提供對本系統資料分析的功能。

注意:本文的 IDE 使用的是 intelliJ IDEA,推薦大家也用這個,用了都說好,用了你就會愛上它。

建立專案的組織結構

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

  • 建立一個 Maven Project,命名為“Gaoxi”。這個 Project 由多個 Module 構成,每個 Module 對應著“微服務”的一個子系統,可獨立執行,是一個獨立的專案。 這也是目前主流的專案組織形式,即多模組專案。

  • 在 Gaoxi 這個專案下建立各個子模組。

每個自模組都是一個獨立的 Spring Boot 專案:

  • Gaoxi-User,使用者服務。

  • Gaoxi-Order訂單服務。

  • Gaoxi-Product產品服務。

  • Gaoxi-Analysis資料分析服務。

  • Gaoxi-Controller,本系統的控制層,和以往三層結構中的 Controller 層的作用一樣,都是用作請求排程,只不過在微服務架構中,我們將它抽象成一個單獨的系統,可以獨立執行。

  • Gaoxi-Common-Service-Facade,它處於本系統的最底層,被所有模組依賴,一些公用的類庫都放在這裡。

  • Gaoxi-Redis,我們將 Redis 封裝成一個單獨的服務,執行在獨立的容器中,當哪一個模組需要使用 Redis 的時候,僅需要引入該服務即可,就免去了各種繁瑣的、重複的配置。而這些配置均在 Gaoxi-Redis 系統中完成了。


下面開始動手。

建立 Project

首先 New 一個 Project,如下圖:


然後選擇 Spring Initializr,如下圖所示:


設定 groupId、artifactId、version,程式碼如下:

<groupId>com.gaoxi</groupId>
<artifactId>gaoxi</artifactId>
<version>0.0.1-SNAPSHOT</version>

Project 建立完畢!接下來在 Project 下面建立 Module。

建立 Module

在 Project 上 New Module,如下圖:


和剛才一樣,選擇 Spring Initializr,設定 groupId、artifactId、version。

依次建立好所有的 Module,如下圖所示:


構建模組的依賴關係

目前為止,模組之間沒有任何聯絡,下面我們要通過 pom 檔案來指定它們之間的依賴關係,依賴關係如下圖所示:


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>

到此為止,模組的依賴關係配置完畢!但要注