简介
以下标题与内容为整个学力分项目的工程化搭建过程,其中,父工程包含子工程,而子工程中的层级关系由本文档的目录树可以看出
整体目录的顺序即为工程的搭建顺序
项目的整体架构图如下
创建父工程
因为微服务应用有许多的个体微服务模块,所以需要建立一个顶层的父模块来统一管理,使用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 GateWay是Spring提供的第二代网关框架,取代了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端
- github.com/seata/seata… server 端
- github.com/seata/seata… 源码
将上述下载的两个文件解压到没有中文和空格的路径下
到源码的/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-server的conf目录中,如下内容
# 注册中心
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中关于seate的store.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: *