微服务工程化搭建

232 阅读11分钟

简介

以下标题与内容为整个学力分项目的工程化搭建过程,其中,父工程包含子工程,而子工程中的层级关系由本文档的目录树可以看出

整体目录的顺序即为工程的搭建顺序

项目的整体架构图如下

创建父工程

因为微服务应用有许多的个体微服务模块,所以需要建立一个顶层的父模块来统一管理,使用IDEA.2022快速创建一个maven工程模块,pom结构如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xlf</groupId>
    <artifactId>xlf-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <description>学历分顶层父项目,用于管理子模块以及依赖版本管理</description>

    <!--子模块-->
    <modules>
        <module>xlf-model</module>
        <module>xlf-common-utils</module>
        <module>xlf-common-service</module>
        <module>xlf-services</module>
        <module>xlf-gateway</module>
    </modules>

    <!--配置全局的参数-->
    <properties>
        <!--配置打包编译字符集参数-->
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!--
        参考官方文档:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
        配置 springcloud 、springcloud-alibaba 、springboot 版本对应关系
        -->
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.7.RELEASE</spring-cloud-alibaba.version>
        <spring-boot.version>2.3.12.RELEASE</spring-boot.version>

        <!--其他依赖版本参数-->
        <mybatis-plus.version>3.3.1</mybatis-plus.version>
        <mybatis-plus.version>3.3.1</mybatis-plus.version>
        <mysql.version>5.1.46</mysql.version>
        <apche.common.version>3.12.0</apche.common.version>
        <java.jwt.version>4.0.0</java.jwt.version>
        <hutool.version>5.8.5</hutool.version>
        <google.guava.version>31.1-jre</google.guava.version>
        <fastjson2.versoin>2.0.10</fastjson2.versoin>
        <easy.excel.version>3.1.1</easy.excel.version>
        <swagger2.version>3.0.0</swagger2.version>
        <common.codec.version>1.15</common.codec.version>
    </properties>

    <!--
    统一依赖管理
    注意:dependencyManagement 只是申明这些依赖及其版本,并不会实际引入依赖
    其他的子模块中可以按需引入以下统一版本的依赖,方便依赖的版本管理
    -->
    <dependencyManagement>
        <dependencies>
            <!-- spring-cloud 依赖导入 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- spring-cloud-alibaba 依赖导入 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- spring-boot 依赖导入 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--mybatis-plus 持久层-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>

            <!-- mysql 连接驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>

            <!--apache 公共工具类-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${apche.common.version}</version>
            </dependency>

            <!--apache commons-codec 依赖,用于编码加密解码等-->
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>${common.codec.version}</version>
            </dependency>

            <!--引入 jwt-->
            <dependency>
                <groupId>com.auth0</groupId>
                <artifactId>java-jwt</artifactId>
                <version>${java.jwt.version}</version>
            </dependency>

            <!--hutool 工具类-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>

            <!--谷歌 Java 扩展工具包-->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${google.guava.version}</version>
            </dependency>

            <!--fastJson2-->
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2-extension</artifactId>
                <version>${fastjson2.versoin}</version>
            </dependency>

            <!--excel导入导出-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>${easy.excel.version}</version>
            </dependency>

            <!--swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger2.version}</version>
            </dependency>

            <!--swagger ui-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger2.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

创建子工程

xlf-common-core

学力分项目的核心依赖工程,包括一些全局配置的抽取,常用工具类,全局异常处理,全局常量等,pom结构如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-parent</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>

    <artifactId>xlf-common-core</artifactId>
    <description>公共依赖包,包含一些工具类、配置、常量等</description>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
        </dependency>

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
    </dependencies>
</project>

xlf-common-service

学力分的公共服务模块,主要提供一些其他微服务应用的公共服务,例如短信、智能审核等第三方接入,pom结构如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-parent</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <description>用于提供公共api的服务,例如将一些第三方服务对接到此模块中</description>

    <artifactId>xlf-common-service</artifactId>

    <dependencies>

    </dependencies>

</project>

dependencies 是预留的,用于载入第三方依赖

xlf-gateway

学力分网关服务,主要用于集群的负载均衡调用,服务熔断降级,流控、请求拦截以及安全限制等,此服务为整个学力分后端项目的调用入口,前端都统一请求此网关服务,由spring-cloud-gatway提供服务转发到对应微服务应用集群的能力,其pom结构如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-parent</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xlf-gateway</artifactId>
    <description>网关服务模块(用于服务转发,限流、跨域控制等)</description>

    <dependencies>

        <dependency>
            <groupId>com.xlf</groupId>
            <artifactId>xlf-common-core</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!--用于监测单个 springboot 服务的健康状况-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- 服务注册 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    </dependencies>

    <!--打包构建插件设置-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.xlf.gateway.XlfGateWayApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

xlf-model

此模块用于管理学力分所有的实体类、VO、DTO,因为微服务之间一定会存在通信,此时采用公共的实体类或 DTO 作为数据传输的载体会更加方便,也利用统一管理;但如果将实体类分散到各个微服务模块中,那么在服务之间通信时,还需要建立中间对象;

此模块的pom结构如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-parent</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xlf-model</artifactId>

    <packaging>jar</packaging>
    <description>公共的 entity、vo、dto 依赖,因为涉及到微服务之间的数据传输,将实体层作为公共模块更为方便</description>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--
        mybatis-plus,实体类需要使用 mybatis-plus 相关的注解
        但此依赖的 scope 为 provided,这代表此依赖不具备传递性,其他模块引入 xlf-model 时
        mybatis-plus 的依赖不会被引入
        -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <scope>provided</scope>
        </dependency>

        <!--实体对象可能涉及到导入导出-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <scope>provided</scope>
        </dependency>

    </dependencies>
</project>

xlf-services

此模块为学力分顶级父工程下的一个二级父工程,用于管理所有的微服务模块,同时将顶级父模块中的统一依赖版本实际的引入进来,用于其他微服务模块使用,其pom结构如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-parent</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>

    <modules>
        <module>xlf-corporation-service</module>
        <module>xlf-user-service</module>
        <module>xlf-payment-service</module>
        <module>xlf-admin-service</module>
    </modules>

    <artifactId>xlf-services</artifactId>

    <dependencies>

        <dependency>
            <groupId>com.xlf</groupId>
            <artifactId>xlf-common-core</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>com.xlf</groupId>
            <artifactId>xlf-model</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- 服务调用feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!-- 服务注册 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- 服务配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <!-- 流量控制 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
    </dependencies>
</project>

xlf-user-service

学力分项目的用户模块,用于处理用户相关业务,其pom结构为

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-services</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xlf-user-service</artifactId>
    <description>用户服务</description>

    <dependencies>

    </dependencies>

    <!--打包构建插件设置-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.xlf.user.XlfUserApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

xlf-admin-service

学力分的后台管理模块,其pom结构如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-services</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xlf-admin-service</artifactId>

    <dependencies>

    </dependencies>
</project>

xlf-corporation-service

学力分机构模块

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-services</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xlf-corporation-service</artifactId>
    
    <dependencies>
        
    </dependencies>

</project>

xlf-payment-service

学力分订单模块

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xlf-services</artifactId>
        <groupId>com.xlf</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xlf-payment-service</artifactId>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.xlf.payment.PaymentApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

以上,整个学历分项目的工程化搭建完毕

微服务环境搭建

关于集群 & 分布式 & 微服务

集群是物理形态 只要是一堆机器就叫集群,但它们之间是不是协作运行,不得而知

分布式是一种工作方式,分布式是建立在网络之上的软件系统,分布式分布在不同地方的各个部分,称之为节点;分布式指的是将不同的业务分布在不同的地方;集群指的是多台计算机集中执行同一个业务

微服务是一种架构风格,简单来说微服务就是将大型服务进行拆分,小到一个服务只对应一个单一的功能。每个微服务仅关注于完成一件任务并很好地去完成,这个服务可以单独部署运行;各个微服务之间是松耦合的,服务之间可以通过RPC(远程调用)来相互交互。每个微服务都是由独立的小团队开发、测试、部署,上 线,负责它的整个生命周期

分布式中的每一个节点都可以作为一个集群,而集群却不一定就为分布式的

三者的简单关系:由微服务构建一个个功能完成某一业务,多个微服务构成整个分布式系统,在此基础上,对各个微服务集群化,实现【高并发】【高可用】 参考博文 cloud.tencent.com/developer/a…

整合 SpringCloudAlibaba

spring-cloud-alibaba 是阿里提供的分布式的组件,用于快速搭建分布式应用

具体可查阅官方文档 spring-cloud-alibaba/README-zh.md at 2.2.x · alibaba/spring-cloud-alibaba (github.com)

注意:

spring-cloud-alibaba 并不是指的一个整体,它是由多个分布式应用所需要的组件构成,在使用时可以直接引入 spring-cloud-alibaba 的依赖管理,然后按需引入其中的组件依赖

其次,spring-cloud、spring-cloudalibaba、springboot 直接的版本有着一定的对应关系,可以参考官方文档 版本说明 · alibaba/spring-cloud-alibaba Wiki (github.com)

整合 nacos

Nacos 是一个服务的注册和发现平台,服务的注册和发现可以这样来理解:因为多个微服务现在是相互独立的,需要一个中间平台将这些服务进行一个统一的管理,就好比进入小区要登记,这个过程叫做服务的注册

凡是记录在 nacos 中的微服务应用,当我们需要调用时(通过 openfeign 或则是 dubbo rpc 等方式),Nacos 也能保证能够找到对应的服务,这个过程叫做服务的发现

具体可以查看官方文档 什么是 Nacos

下面来具体的使用上 nacos,首先在需要使用服务注册的应用中,引入 nacos 依赖

<!-- 服务注册 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

完整的依赖应该还要包含 spring-cloud-alibaba 的依赖管理引入,但这在上面的工程创建提供的pom结构中已经有了,这里不再赘述

编写配置文件

### bootstrap.properties 会先与 application.properties 加载,在这里编写应用启动时的基本配置,用于配合 nacos 配置中心的使用
### 实际上 nacos 配置中心更适合生产环境,本地开发时,可以直接在项目中配置
# nacos 服务地址
spring.cloud.nacos.discovery.server-addr=192.168.31.128:8848
# 必须要配置服务名称,用于 nacos 标识此应用
spring.application.name=xlf-user-service

在 springboot 的主启动类上添加如下注解

@EnableDiscoveryClient // 【启用服务发现】使得 nacos 在此服务启动时,能够发现该服务

因为 nacos 是一个单独的服务,所以还要安装 nacos,下载地址 Releases · alibaba/nacos (github.com)

因为是在Linux环境下运行 nacos(也可以选择 win 环境,同时注意配置 JAVA_HOME 的环境变量),使用如下命令即可运行 nacos

startup.sh -m standalone # linux
startup.cmd -m standalone # win

在 jdk11 中,直接运行 nacos 会报错,需要修改startup.sh如下(或者换用 jdk8)

#!/bin/bash

# Copyright 1999-2018 Alibaba Group Holding Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

cygwin=false
darwin=false
os400=false
case "`uname`" in
CYGWIN*) cygwin=true;;
Darwin*) darwin=true;;
OS400*) os400=true;;
esac
error_exit ()
{
 echo "ERROR: $1 !!"
 exit 1
}
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/opt/taobao/java
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME

if [ -z "$JAVA_HOME" ]; then
if $darwin; then

 if [ -x '/usr/libexec/java_home' ] ; then
   export JAVA_HOME=`/usr/libexec/java_home`

 elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; then
   export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"
 fi
else
 JAVA_PATH=`dirname $(readlink -f $(which javac))`
 if [ "x$JAVA_PATH" != "x" ]; then
   export JAVA_HOME=`dirname $JAVA_PATH 2>/dev/null`
 fi
fi
if [ -z "$JAVA_HOME" ]; then
     error_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better!"
fi
fi

export SERVER="nacos-server"
export MODE="cluster"
export FUNCTION_MODE="all"
export MEMBER_LIST=""
export EMBEDDED_STORAGE=""
while getopts ":m:f:s:c:p:" opt
do
 case $opt in
     m)
         MODE=$OPTARG;;
     f)
         FUNCTION_MODE=$OPTARG;;
     s)
         SERVER=$OPTARG;;
     c)
         MEMBER_LIST=$OPTARG;;
     p)
         EMBEDDED_STORAGE=$OPTARG;;
     ?)
     echo "Unknown parameter"
     exit 1;;
 esac
done

export JAVA_HOME
export JAVA="$JAVA_HOME/bin/java"
export BASE_DIR=`cd $(dirname $0)/..; pwd`
export CUSTOM_SEARCH_LOCATIONS=file:${BASE_DIR}/conf/

#===========================================================================================
# JVM Configuration
#===========================================================================================
if [[ "${MODE}" == "standalone" ]]; then
 JAVA_OPT="${JAVA_OPT} -Xms512m -Xmx512m -Xmn256m"
 JAVA_OPT="${JAVA_OPT} -Dnacos.standalone=true"
else
 if [[ "${EMBEDDED_STORAGE}" == "embedded" ]]; then
     JAVA_OPT="${JAVA_OPT} -DembeddedStorage=true"
 fi
 JAVA_OPT="${JAVA_OPT} -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
 JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${BASE_DIR}/logs/java_heapdump.hprof"
 JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages"

fi

if [[ "${FUNCTION_MODE}" == "config" ]]; then
 JAVA_OPT="${JAVA_OPT} -Dnacos.functionMode=config"
elif [[ "${FUNCTION_MODE}" == "naming" ]]; then
 JAVA_OPT="${JAVA_OPT} -Dnacos.functionMode=naming"
fi

JAVA_OPT="${JAVA_OPT} -Dnacos.member.list=${MEMBER_LIST}"

JAVA_MAJOR_VERSION=$($JAVA -version 2>&1 | sed -E -n 's/.* version "([0-9]*).*$/\1/p')
if [[ "$JAVA_MAJOR_VERSION" -ge "9" ]] ; then
JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${BASE_DIR}/logs/nacos_gc.log:time,tags:filecount=10,filesize=102400"
else
#JAVA_OPT_EXT_FIX="-Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext"
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext"
JAVA_OPT="${JAVA_OPT} -Xloggc:${BASE_DIR}/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M"
fi

JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb"
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/${SERVER}.jar"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} --spring.config.additional-location=${CUSTOM_SEARCH_LOCATIONS}"
JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/nacos-logback.xml"
JAVA_OPT="${JAVA_OPT} --server.max-http-header-size=524288"

if [ ! -d "${BASE_DIR}/logs" ]; then
mkdir ${BASE_DIR}/logs
fi

# cho "$JAVA $JAVA_OPT_EXT_FIX ${JAVA_OPT}"
echo "$JAVA ${JAVA_OPT}"

if [[ "${MODE}" == "standalone" ]]; then
 echo "nacos is starting with standalone"
else
 echo "nacos is starting with cluster"
fi

# check the start.out log output file
if [ ! -f "${BASE_DIR}/logs/start.out" ]; then
touch "${BASE_DIR}/logs/start.out"
fi
# start
# echo "$JAVA $JAVA_OPT_EXT_FIX ${JAVA_OPT}" > ${BASE_DIR}/logs/start.out 2>&1 &
# nohup "$JAVA" "$JAVA_OPT_EXT_FIX" ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 &
echo "$JAVA ${JAVA_OPT}" > ${BASE_DIR}/logs/start.out 2>&1 &
nohup $JAVA ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 &
echo "nacos is starting,you can check the ${BASE_DIR}/logs/start.out"

nacos 启动成功后,本地通过http://localhost:8848/nacos即可访问(nacos一般部署在内网环境下,不对外开放)

更多的 Nacos 部署方式(例如集群部署)等,可以参考官方文档 Nacos支持三种部署模式

nacos 持久化配置

执行 conf 目录下的nacos-mysql.sql文件

修改application.properties配置,如下

spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1/nacos_dev?characterEncoding=utf8&serverTimezone=UTC&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=111111

重启nacos即可

整合 GateWay 网关

微服务架构下,一个服务可能有多台机器组成的集群,其中某一台机器挂掉,在没有网关的情况下,前端依然在请求该服务器,造成响应超时等问题

在不同的服务中,可能还涉及到鉴权,请求拦截等需求,这部分的逻辑也可以使用到网关来统一管理,网关还可以起到负载均衡的作用 SpringCloud GateWaySpring提供的第二代网关框架,取代了Zuul网关

gateway 网关的基本概念

Route(路由)

网关配置的基本组成模块,和Zuul的路由配置模块类似。一个 Route 模块 由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标 URI 会被访问

Predicate(断言)

这是一个 Java8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或 参数。断言的输入类型是一个 ServerWebExchange

Filter(过滤器)

Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例

当请求过来时,断言(Predicate) 该请求是否匹配某个路由(Route),若匹配到,经过一系列过滤器(Filter) 最终到达请求的目标资源

相关博客 www.cnblogs.com/crazymakerc…

中文文档 cloud.tencent.com/developer/a…

要使用网关,首先要引入 gateway 依赖

<!--gateway 网关-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

还需要引入 nacos 服务注册依赖(因为网关也是一个单独的服务,需要注册到 nacos 由 nacos 统一管理)

<!-- 服务注册 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在主启动类上开始服务注册

@EnableDiscoveryClient

配置 nacos 注册中心的地址

#注册中心地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

添加路由匹配的配置

spring:
  cloud:
    gateway:
      routes:
        - id: xlf-user-service
          uri: lb://xlf-user-service
          predicates:
            - Path=/api/user/**
        - id: xlf-payment-service
          uri: lb://xlf-payment-service
          predicates:
            - Path=/api/payment/**

id 路由的唯一标识 由此可配置多个路由

uri 目标 uri,将 predicates 到的请求转发到:目标 uri + predicates 断言到的请求路径,uri 会 被作为/路径替换掉 predicates 断言到的路径的原本/路径,例如 uri 为http://localhost:8888 ,predicates 为http://localhost:88/api/user/getUser ,则经过网关断言和路由后,变为http://localhost:8888/api/user/getUser,若再经过了 filters 二次处理,如上,使用到了 / ,则 uri 部分不会变,相当于http://localhost:8888/filters处理后的路径

若 uri 为lb://serviceName则调用注册中心里面指定serviceName的微服务,若有多个同名服务,则作为 一个服务的集群,进行负载均衡的调用

predicates 根据一定规则,匹配向网关请求的路径到当前 predicates 所属的路由

filters 过滤器允许以某种方式对传入的 HTTP 请求或返回的 HTTP 响应进行修改,过滤器的作用域是某些特定路由,Spring Cloud Gateway 包括许多内置的 Filter 工厂

整合 OpenFeign

微服务之间必定会涉及到数据通信,这就需要使用到OpenFeign

OpenFeign 是一个声明式的 http 客户端,feign 提供了 http 请求的模板且整合了 ribbon(负载均衡) 和 hystrix(服务熔 断)

OpenFeign 在 NetFlixFeign 的基础上,扩展了对 SpringMVC 的支持,在其实现下,我们只需要创建一个接口并使用注解的方式来配置它,即可完成对服务方接口的绑定

同样的,使用 OpenFeign 需要先引入相关依赖

<!--openFeign 远程调用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

编写远程调用的接口

/**
 * @author dhj
 * @date 2022/8/14
 */
@FeignClient("xlf-gateway") // 正常应该调用对应的服务,但是通过网关调用可以实现负载均衡,二者都可
@Service
public interface PaymentFeignService {
    @PostMapping("/api/payment/create")
    String create(User user);
}

如上,在【当前服务】创建的接口类上标注@FeignClient,注解参数代表远程调用的【其他服务】或者【网关】在注册中心中对应的服务名

当前PaymentFeignService远程调用接口中的create(User user)方法,就是【其他服务】中对应的controller接口方法,两者需要完全保持一致(包括方法返回值,请求类型等)且【当前服务】中请求的路径需要写完整

@Service注解是为了方便注入接口

最后在主启动类上标注扫描注解

@EnableFeignClients("com.xlf.user.feign") 

最后,通过该接口,就可在当前服务,远程调用其他服务,获得其他服务返回的结果;远程调用的相关配置,都是在当前服务完成,而被调用的服务,只需要提供对应的请求接口而不做任何其他配置

整合 Seta

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案

推送配置到 nacos

下面以seata 1.3.0为例,总结 seata 配置的一般流程

下载源码server

将上述下载的两个文件解压到没有中文和空格的路径下

到源码的/script/server/db目录执行mysql.sql文件(需要提前建立一个数据库,这里叫 seata)

到源码的/script/config-center目录下修改config.txt,因为config.txt中都是默认配置,所以只需要保留修改的配置项即可,如下

# 【重要】my_test_tx_group这个事务分组所使用的seata-server的集群名称,要与后面seata-server中registry.conf的registry.nacos.cluster保持一致
service.vgroupMapping.my_test_tx_group=testCluster
# 使用db存储
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
# 因为是mysql8.x版本,驱动位置发生了改变(有了.cj)
store.db.driverClassName=com.mysql.cj.jdbc.Driver
# mysql8.x版本serverTimezone需要按照下面设定(否则会报serverTimezone错误,一堆乱码)。另外 这个属性在推送到nacos之后参数可能会消失,需要手动修改以下
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&serverTimezone=GMT%2B8
store.db.user=root
store.db.password=root

实际配置时,需要删除注释

无注释版如下

service.vgroupMapping.my_test_tx_group=testCluster
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&serverTimezone=GMT%2B8
store.db.user=root
store.db.password=111111

到源码目录的/script/config-center/nacos下执行nacos-config.sh,如下

nacos-config.sh -h nacos的Ip地址 -p nacos的端口号 -u nacos用户名 -w nacos密码 -g 本配置列表的所属分组(要与后面seata服务端config.nacos.group保持一致,默认为SEATA_GROUP)

具体执行如下

nacos-config.sh -h 192.168.1.122 -p 8848 -u nacos -w 111111

执行后可以将配置推送到nacos的配置中心

修改 server 端配置

seate-serverconf目录中,如下内容

# 注册中心
registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  # 如果是file,则需要配置file.conf
  # 因为本例使用nacos,此处填写nacos
  type = "nacos"

  # 【重要】此处是nacos中的服务注册配置
  nacos {
  	# 【重要】本服务端在nacos中的微服务名称,后面seata客户端的application.yml中要用到
    application = "seata-server"
    # nacos地址(默认为8848,笔者此处做了负载均衡改了端口)
    serverAddr = "127.0.0.1:8848"
    # seata-server微服务的分组
    group = "SEATA_GROUP"
    # seata-server微服务的命名空间,此处省略,使用默认值public
    namespace = ""
    # 【重要】seata-server作为集群时的集群名字,与前面nacos中设定事务分组属性(service.vgroupMapping.my_test_tx_group)保持一致
    cluster = "testCluster"
    # nacos1.2加入了鉴权,账号密码不可省略
    username = "nacos"
    password = "111111"
  }
}

# 配置中心
config {
  # file、nacos 、apollo、zk、consul、etcd3
  # 因为本例使用nacos,此处填写nacos
  type = "nacos"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    # 【重要】本服务端的配置在nacos配置列表哪个分组下,要与上面推送到nacos的分组列表保持一致,默认是SEATA_GROUP。
    group = "SEATA_GROUP"
    username = "nacos"
    password = "111111"
  }
}

无注释版

registry {
  type = "nacos"
  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "testCluster"
    username = "nacos"
    password = "111111"
  }
}

config {
  type = "nacos"
  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "111111"
  }
}

如果seata连接的数据库在远程,那么注意远程数据库是否允许了外部ip访问

还要注意nacos中关于seatestore.db.url配置是否被重写,可能会丢失url的时区信息,导致seata启动报错

如果以上流程没有报错且seata启动成功,那么在 nacos 的服务列表中就会有seata的服务名称,默认为seata-server

配置客户端

添加依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>${seata.version}</version>
</dependency>

编写配置

seata:
  # 开启自动装配
  enabled: true
  # 本客户端的微服务名称
  application-id: xlf-user-service
  # 读取哪个事务分组
  # 例如此时会读取 SEATA_GROUP 这个分组下的 service.vgroupMapping.my_test_tx_group 这个属性的值。从上面的配置可以知道此处的最终值为 testCluster。后面程序运行会找到 testCluster 这个集群的seata服务端,进行通讯。
  tx-service-group: my_test_tx_group
  # 配置中心设置
  config:
    type: nacos
    nacos:
      username: nacos
      password: 111111
      server-addr: 127.0.0.1:8848
      # 读取的配置分组
      group: SEATA_GROUP
  # 注册中心设置
  registry:
    type: nacos
    nacos:
      # SEATA服务中心的微服务名,此处与服务端保持一致
      application: seata-server
      server-addr: 127.0.0.1:8848
      username: nacos
      password: 111111

配置代理数据源

import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;

/**
 * @author dhj
 * @date 2022/8/15
 */
@Configuration
public class SeataDataSourceProxyConfig {

    // 得到当前数据源的配置信息
    @Autowired
    DataSourceProperties dataSourceProperties;

    /**
     * 向容器中注入自己的数据源,Spring 会优先使用自己的数据源
     */
    @Bean
    public DataSource dataSource(DataSourceProperties properties) {

        /*
        构造一个指定类型的数据源,这里使用的数据源是 HikariDataSource(Spring 默认的数据源)
        主要是配一些数据库连接池的基本配置,包括 url username password 等
        */
        HikariDataSource dataSource = properties
                .initializeDataSourceBuilder()
                .type(HikariDataSource.class).build();

        if (StringUtils.hasText(properties.getName())) {
            // 如果配置中有数据库连接池的名称,就设置数据库连接池的名称为配置中指定的
            dataSource.setPoolName(properties.getName());
        }
        // 使用 seata 的代理数据源将原本的数据源进行包装,最终返回包装后的 seata 代理数据源
        return new DataSourceProxy(dataSource);
    }
}

最后,在整个分布式事务的入口处(也就是整个分布式调用链路的开头),添加@GlobalTransactional注解,可以指定回滚的异常,如下

@GlobalTransactional(rollbackFor = Exception.class)

在其他非入口的事务方法处,只需要使用@Transactional注解即可

整合 Sentinel

引入 sentinel 依赖(前置需要 spring-cloud-alibaba 依赖管理)

<!-- 流量控制 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

下载控制台(版本需要和 sentinel 的 core 依赖包对应) github.com/alibaba/Sen…

启动控制台(控制台默认账号密码都为sentinel

java -jar sentinel-dashboard-1.8.1.jar --server.port=8333 # 可以指定启动端口

编写配置

sentinel:
  transport:
    dashboard: 8889 # sentinel 控制台的启动端口
    port: 8719 # 当前服务与 sentinel 数据交互的端口,默认 8719

开启实时监控

导入actuator,便于sentanel读取应用相关信息

<!--springboot 健康状态监控-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置某个应用暴露所有端点

### springboot-actuator 暴露所有端点
management:
  endpoints:
    web:
      exposure:
        include: *