构建一个微服务框架学习笔记

3 篇文章 0 订阅
3 篇文章 0 订阅

一、技术栈选择

  • 开发工具: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>

找到<repository></repository>

<repository>
    <id>spring</id>
    <url>https://maven.aliyun.com/repository/spring</url>
    <releases>
        <enabled>true</enabled>
    </releases>
    <snapshots>
        <enabled>true</enabled>
    </snapshots>
</repository>

具体引用阿里maven仓库参考官方文档

安装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="wsxrafael@126.com"
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="wsxrafael@126.com"
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
    ports:
      - 28070:8070
    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);
    }

(九)swagger支持

pom.xml修改
添加:

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

新建config类:SwaggerConfig.java

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build();
    }
}

访问swagger2的地址,如果服务端口开的是8888,则访问http://localhost:8888/v2/api-docs,可以看到生成的api文档。文档可读性差,所以加入SwaggerUI的支持。

SwaggerUI的支持,修改pom.xml

添加:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

再访问http://localhost:8888/swagger-ui.html,可读性好了很多。

配置拦截器

如果访问不到这个地址,需要配置拦截器。
swagger-ui.html位置在classpath:/META-INF/resources/中,需要路由到这里。

package com.zt.server.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig  implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
    }
}

这里注意,如果使用extends WebMvcConfigurationSupport,则会取消对所有默认配置的支持,则需要代码中加上对于静态资源的引用,建议采用implements WebMvcConfigurer的方式。

引用:

四、问题

(一)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的包,所以报错。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值