構建一個微服務框架學習筆記

一、技術棧選擇

  • 開發工具:VsCode。
  • 後端框架:Spring boot。
  • 前端框架:Vue.js+ElementUI。
  • 服務網絡:Spring Cloud Gateway(Zuul)。
  • 服務註冊與發現:Spring Cloud Eureka。
  • 配置中心:Apollo。
  • 數據庫:MySQL5.7。
  • 基礎設施:CentOS7.6+Docker+Kubernetes+jenkins。

二、環境配置

(一)開發終端

安裝java jdk。此次選擇java1.8,教程已經很多,只需在安裝後設置JAVA_HOME、CLASSPATH、path三個系統環境變量即可。

如jdk安裝目錄爲C:\Program Files\Java\jdk1.8.0_231,則

  • 新建JAVA_HOME=C:\Program Files\Java\jdk1.8.0_231,CLASSPATH=.;
  • 新建%Java_Home%\bin;%Java_Home%\lib\dt.jar;%Java_Home%\lib\tools.jar
  • path中增加%JAVA_HOME%\bin

安裝Maven。

在windows下下載.zip壓縮包,Linux下下載.tar.gz壓縮包,解壓縮後,如安裝目錄爲c:\apache-maven-3.6.3,此後設置幾個環境變量path(添加c:\apache-maven-3.6.3\bin)、MAVEN_HOME(設爲c:\apache-maven-3.6.3)。在C:\apache-maven-3.6.3\conf\settings.xml中找到<mirrors></mirrors>,添加以下內容:

<mirrors>
    <mirror>
        <id>alimaven</id>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <mirrorOf>central</mirrorOf>
    </mirror>
</mirrors>

安裝VsCode。一步步安裝即可。
安裝java插件,Java Extension Pack,Microsoft公司。同時會自動安裝相關聯插件。
安裝Spring Boot Extension Pack,Pivotal公司。同時會自動安裝相關聯插件。
安裝Lombok Annotations Support for VS Code。
配置Maven。java.configuration.maven.userSettings,配置爲C:\apache-maven-3.6.3\conf\settings.xml

(二)Eureka

搭建要給Eureka服務器,需要從源碼開始建立,根據需要設置,一般小型化項目使用單節點默認配置即可。
建立一個Spring boot服務工程,pom.xml需要引入:

<!-- 引入cloud -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<!-- eureka server -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

代碼只需在application類上使用@EnableEurekaServer的註解即可。配置:

# 開放端口
server.port=8761
# 服務名稱
spring.application.name='wpeureka'
# 主機名
eureka.instance.hostname=localhost
# 支持使用ip地址註冊
eureka.instance.prefer-ip-address=true
# 以下兩個配置說明自己是一個服務端
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
#服務地址
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

將其打包爲docker,Dockerfile:

FROM openjdk:8-jre
LABEL maintainer="[email protected]"
LABEL description="Spring Cloud Eureka for Docker"
ADD eureka-0.0.1-SNAPSHOT.jar /usr/local/lib/eureka-0.0.1-SNAPSHOT.jar
EXPOSE 8761
ENTRYPOINT ["java", "-jar", "/usr/local/lib/eureka-0.0.1-SNAPSHOT.jar"]

(三)Gateway(Zuul)

與Eureka類似,也是要從代碼開始編譯搭建。
pom.xml需要引入:

<!-- 引入cloud -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<!-- 與eureka配合 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

這裏需要注意的是,SpringCloud和Springboot需要版本對應,如2.2.5與Hoxton.SR3是對應的。
配置上,將Gateway向Eureka註冊,這樣,就可以通過網關訪問Eureka的服務。

# 開放端口
server.port=9999
# 服務名稱
spring.application.name=wpgateway
# gateway開啓服務註冊和發現的功能,自動根據服務發現爲每一個服務創建了一個router,這個router將以服務名開頭的請求路徑轉發到對應的服務。
spring.cloud.gateway.discovery.locator.enabled=true
# 將請求路徑上的服務名配置爲小寫
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
# eureka使用ip地址,不使用hostname
eureka.instance.prefer-ip-address=true
# 向eureka服務端註冊
eureka.client.service-url.defaultZone=http://172.17.0.1:28061/eureka/

這裏將eureka的地址設爲172.17.0.1(橋接網絡的主機默認IP),即在docker中運行時默認連接的是主機的eureka服務。
Dockerfile:

FROM openjdk:8-jre
LABEL maintainer="[email protected]"
LABEL description="Spring Cloud Gateway for Docker"
ADD gateway-0.0.1-SNAPSHOT.jar /usr/local/lib/gateway-0.0.1-SNAPSHOT.jar
EXPOSE 9999
ENTRYPOINT ["java", "-jar", "/usr/local/lib/gateway-0.0.1-SNAPSHOT.jar"]

(四)MySQL

選用容器方式,從倉庫中獲取官方鏡像。
從docker hub上pull一個MySQL的官方鏡像

docker pull MySQL:5.7.29

運行鏡像。

docker run \
--name MySQL5.7 \//命名
-p 23306:3306 \//將數據庫端口3306綁定到主機的23306上
-v /data/MySQL/data:/var/lib/MySQL \//將數據庫數據目錄綁定到主機目錄/data/MySQL/data上
--restart=always \//自動重啓
-e MySQL_ROOT_PASSWORD=123456 \//設置MySQL的root密碼爲123456
-d MySQL:5.7.29 \
--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci//設置使用utf8字符集。數據庫的環境設置,可以通過綁定目錄的方式實現,-v /data/MySQL/custom:/etc/MySQL/conf.d,在主機目錄/data/MySQL/custom下建立配置文件my.cnf,即可配置數據庫環境。

(五)Jenkins

選用容器方式,從倉庫中獲取官方鏡像。

從docker hub上pull一個官方鏡像

docker pull jenkins/jenkins

運行鏡像。

docker run \
--name jenkins \//命名
-p 8042:8080 \//綁定端口,這裏是8042
-v /data/jenkins/data:/var/jenkins_home \//綁定數據目錄,這裏是到主機目錄/data/MySQL/data上
--restart=always \//自動重啓
-u root -d jenkins/jenkins

配置

初次訪問,http://x.x.x.x:8042,需要激活,激活碼在/var/jenkins_home/secrets/initialAdminPassword,綁定卷後在主機的/data/jenkins/data/secrets/initialAdminPassword中。
之後選擇並安裝插件。
之後創建管理員賬戶。
配置插件更新的國內鏡像地址,在Manage Jenkins->Plugin Manager->Advanced中將升級站點由https://updates.jenkins.io/update-center.json換爲https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
同時修改/data/jenkins/data/updates/default.json文件,把updates.jenkins-ci.org/downloadmirrors.tuna.tsinghua.edu.cn/jenkins,把www.google.com修改成www.baidu.com

流水線插件

依賴插件:pipeline。
pipeline的所有配置都在腳本文件中,可以在代碼中建立一個Jenkinsfile的腳本文件,主要配置參見下圖。
pipelin配置
支持從版本管理庫中拉取代碼,然後執行其中的Jenkinsfile腳本。
一個簡單的構建腳本如下:

node {
    // 拉取代碼
    stage('pull code'){
        checkout scm
    }
    // 編譯
    stage('backend build'){
        dir('src') {
            sh 'mvn -DskipTest clean install'
        }
    }
    // 單元測試
    stage('unit test'){
        dir('src') {
            sh 'mvn test'
        }
    } 
    // docker打包
    stage('build docker image'){
        dir('wphomepage/backend/docker'){
             sh './build.sh'
        }
    }
}

(六)Apollo

官網幫助中4.5講的比較明白。

幾個概念

  • Apollo依託於MySQL數據庫,數據存儲都在MySQL中。
  • Apollo服務端的組成:Config Service、Admin Service、Meta Server、apollo Portal,簡單的說就是Config Service負責向客戶端提供配置項的讀取、推送服務,Admin Service負責面向Portal對配置項進行修改、發佈,portal面向用戶提供用戶界面,Config Service和Admin Service向Eureka註冊實現多實例部署發現,在Eureka之上。架了一層Meta Server用於封裝Eureka的服務發現接口。
  • namespase:若干配置項的集合,apollo默認會建立一個名爲application的namespace,絕大部門場景下用這個配置就夠了。
    引用:官網鏈接

創建數據庫

數據庫主要有兩種:portal庫和config庫。Portal數據庫只需部署一套,Config數據庫需要每一個環境配置一套(DEV/UAT/FAT/PRO)。示例中只裝一個DEV環境配置庫和一個Portal數據庫。數據庫sql腳本地址目錄下的apolloportaldb.sql和apolloconfigdb.sql。

  • 登錄數據庫mysql -h127.0.0.1 -P3306 -uroot -p123456
  • 導入portal數據庫,執行腳本:source ./apolloportaldb.sql注意:導入腳本將清空已有數據庫。
  • 導入config數據庫,修改apolloconfigdb.sql並命名爲apolloconfigdbdev.sql將腳本里的數據庫名ApolloConfigDB改爲ApolloConfigDBDev,執行腳本:source ./apolloportaldbdev.sql注意:導入腳本將清空已有數據庫。
  • 數據庫的配置。參考官方幫助的2.1.3,需要更改的配置項都位於兩個數據庫(ApolloPortalDB、ApolloConfigDB)的ServerConfig表。
    需要注意的是config數據庫的ServerConfig表中的eureka.service.url配置項,這是配置 Eureka服務Url地址的,一般默認配置localhost即可,因爲config service自帶一個eureka服務。
    引用

創建docker

借用前人成果,引用
創建docker-compose.yaml,示例:

version: '2.2'
services:
  apollo:
    image: idoop/docker-apollo:latest
    container_name: apollo
    network_mode: "bridge"
    restart: always
    environment:
      PORTAL_DB: jdbc:mysql://172.17.0.1:23306/ApolloPortalDB?characterEncoding=utf8
      PORTAL_DB_USER: root
      PORTAL_DB_PWD: 123456
      PORTAL_PORT: 28082
      DEV_DB: jdbc:mysql://172.17.0.1:23306/ApolloConfigDBDev?characterEncoding=utf8
      DEV_DB_USER: root
      DEV_DB_PWD: 123456

docker-compse up -d啓動。
訪問http://localhost:28070即可訪問
默認超級用戶apllo:admin

三、後端開發架構

(一)初始化Spring boot代碼架構

有兩種方法,一是通過https://start.spring.io/,在線生成模板,直接訪問該網址,勾選選項即可。二是在VsCode中通過Spring boot插件生成,按F1Shift+Ctrl+P調出命令行,選擇Spring Initializr: Generate a Maven Project嚮導,選擇項與在線模板相同。初始化後的代碼結構如下:
初始化代碼結構
在VsCode中調試,將會生成.vscode目錄,保存調試工程文件。
另外,在src\main\java\com\wp\demo目錄下,根據需要再建子目錄,推薦建立controller(web訪問路由)、dao(數據庫訪問)、model(Java bean)、service(服務邏輯)目錄。
代碼結構如下:
調整後代碼結構

(二)編譯運行。

在工程目錄下。
執行mvn install將在target目錄下生成jar包。
執行java -jar target\xxx.jar即可運行。
執行mvn -clean即可清除編譯結果。

(三)VsCode中調試

VsCode中創建調試配置。
調試
將生成.vscode目錄,內含調試配置。

(四)程序配置

使用application.properties或application.yml文件配置程序行爲,常用配置有:
配置端口:server.port = 9090
程序配置在Apollo中集中配置的方法參見:官網說明

(五)Lombok

使用Lombok插件來簡化POJO代碼,需要在IDE中安裝插件,其作用是使用註釋自動生成代碼。其原理是:針對“JSR 269 Pluggable Annotation Processing API”規範,Lombok實現了該規範,在javac(java6之後)運行時得到調用。常用的幾個註解:

  • @Data:@ToString @EqualsAndHashCode @Getter @Setter @RequiredArgsConstructor的集合。
  • @AllArgsConstructor:生成全參構造函數。
  • @NoArgsConstructor:生成無參構造函數。
  • @RequiredArgsConstructor:生成只包含final和@NonNull註解的成員變量的構造函數。
  • @ToString:覆蓋默認的toString()方法。
  • @Getter/@Setter:生成所有成員變量的getter/setter方法。

(六)單元測試

對測試類使用@SpringBootTest註解標明這是一個測試類。對類中測試方法使用@Test標明這是一個測試方法。
針對Controller採用MockMvc模塊進行測試。對類使用@RunWith(SpringRunner.class)、@AutoConfigureMockMvc標明。在方面中使用MockMvc、MockMvcRequestBuilders、MockMvcResultMatchers、MockMvcResultHandlers進行測試。

(七)向Eureka、Gateway註冊

pom.xml文件添加依賴:

<!-- 引入cloud -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<!-- 與eureka配合 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

application.properties文件配置:

# 服務名稱
spring.application.name=wphomepage
# eureka使用ip地址,不使用hostname
eureka.instance.prefer-ip-address=true
# 向eureka服務端註冊
eureka.client.service-url.defaultZone=http://172.17.0.1:28061/eureka/

在Controller類上加註解:@EnableEurekaClient。
這樣就可以通過通過網關訪問註冊的服務:http://主機d地址:28099/服務名/訪問路徑。

(八)數據庫操作

準備

連接池使用阿里巴巴的Druid,操作數據庫有兩種方式,一種使用JdbcTemplate直接操作,一種使用MyBatisjinx ORM操作。
【pom.xml文件引入依賴】

<!--JDBC-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    <scope>compile</scope>
</dependency>
<!--MySQL-->
<dependency>
    <groupId>MySQL</groupId>
    <artifactId>MySQL-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!--MyBatis-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
    <scope>compile</scope>
</dependency>
<!--阿里Druid-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.21</version>
</dependency>

【屬性配置】
在程序配置文件application.properties(或application.yml)中。

#Alibaba Druid
spring.datasource.druid.url = jdbc:MySQL://103.45.109.29:23306/demo?autoreconnect = true&useUnicode=true&characterEncoding=utf8
spring.datasource.druid.username = root
spring.datasource.druid.password = 123456
spring.datasource.druid.driver-class-name = com.MySQL.jdbc.Driver
spring.datasource.druid.max-active = 20

MyBatis

【數據庫】
表名:demotest。
兩個字段:int型RECID爲主鍵,varchar(255)型name。
【POJO類代碼片段】
Demo.java

package com.wp.demo.model;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Demo{
    private int id;
    private String name;
}

【Mapper類代碼片段】
DemoMapper.java

package com.wp.demo.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.type.JdbcType;

import com.wp.demo.model.*;

@Mapper
public interface DemoMapper {
    @Select("select * from demotest")
    @Results(id = "demotestTable", value = {
            @Result(column = "RECID", property = "id", jdbcType = JdbcType.INTEGER, id = true) })
    Demo[] getDemos();

    @Select("select * from demotest where RECID=#{id}")
    @ResultMap(value = "demotestTable")
    Demo getDemo(int id);
}

對於數據庫字段名和POJO類成員變量名稱不同的情況,可以通過@Results註解進行映射,@Results有兩個屬性,id和value,id相當於給這個映射起名,value相當於是一個映射關係集合。這個集合由多個@Result組成,屬性column指明數據庫字段名,property指明POJO類成員變量名,jdbcType指明數據庫中字段類型,id爲true表明是主鍵。後續使用時使用@ResultMap註解即可使用。
【Controller類代碼片段】
DemoServiceController.java

package com.wp.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

import com.wp.demo.service.DemoService;
import com.wp.demo.model.Demo;

@RestController
public class DemoServiceController {
    @Autowired
    DemoService demoService;

    @RequestMapping(value = "/demos")
    public ResponseEntity<Object> getDemo() {
        return new ResponseEntity<>(demoService.getDemos(), HttpStatus.OK);
    }

    @RequestMapping(value = "/demos/{id}", method = RequestMethod.GET)
    public ResponseEntity<Object> getDemo(@PathVariable int id){
        return new ResponseEntity<>(demoService.getDemo(id), HttpStatus.OK);
    }

四、問題

(一)RunWith cannot be resolved to a type錯誤

這個錯誤是由於引入了不同的junittest擴展而引起,在pom.xml中找到

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

這是VS Code通過Springboot插件自動添加的測試組件,在其中找到exclusion一節,將其刪除。
區別在於import的包不一樣。其自動引入的諸如:
import org.junit.jupiter.api.Test;
實際只需引入:
import org.junit.Test;
在原配置下,找不到RunWith的包,所以報錯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章