一、技術棧選擇
- 開發工具: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/download
爲mirrors.tuna.tsinghua.edu.cn/jenkins
,把www.google.com
修改成www.baidu.com
。
流水線插件
依賴插件:pipeline。
pipeline的所有配置都在腳本文件中,可以在代碼中建立一個Jenkinsfile的腳本文件,主要配置參見下圖。
支持從版本管理庫中拉取代碼,然後執行其中的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插件生成,按F1
或Shift+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的包,所以報錯。