cloud微服务开发手册

这个是自己给公司做的一套微服务框架,其中部分的组件开发手册

1.后端结构技术选型

技术 说明 官网地址
Spring Boot 容器+MVC框架 https//spring.io/projects/spring-boot
Spring Security 认证和授权框架 https//spring.io/projects/spring-security
MyBatis-Plus ORM框架 https://mp.baomidou.com/gu
MyBatisPlusGenerator 数据层代码生成 https://mp.baomidou.com/guide/generator.html#%E4%BD%BF%E7%94%A8%E6%95%
PageHelper MyBatis物理分页插件 http//git.oschina.net/free/Mybatis_PageHelper
Swagger-UI (Knife4j) 文档生产工具 https://doc.xiaominfo.com/knife4j/documentation/
RabbitMq 消息队列 https//www.rabbitmq.com/
Redis 分布式缓存 https//redis.io/
MongoDb NoSql数据库 https//www.mongodb.com/
Lombok 简化对象封装工具 https//github.com/rzwitserloot/lombo
Seata 全局事务管理框架 https//github.com/seata/seata
JWT JWT登录支持 https//github.com/jwt
HikariCP 数据库连接池 https://github.com/brettwooldridge/HikariCP
Docker 应用容器引擎 https//www.docker.com/
Spring Cloud Alibaba 微服务架构解决方案 https://spring.io/projects/spring-cloud-alibaba/
Oracle 关系数据 https://www.oracle.com/database/
mysql 关系型数据库 https://www.mysql.com/
Nacos 服务注册&发现和配置管理 https://nacos.io/zh-cn/docs/what-is-nacos.html
Sentines-dashboard 流量控制、熔断降级、系统负载保护 https://github.com/alibaba/Sentinel/wiki

2.环境依赖

工具 版本号 下载地址
JDK 1.8 (64) https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
MYSQL 8.0 https://dev.mysql.com/downloads/
Oracle 11g https://www.oracle.com/cn/database/technologies/instant-client/downloads.html
RabbitMq 3.8.14 https://www.rabbitmq.com/download.html
Redis 5.0.12 https://download.redis.io/releases/redis-5.0.12.tar.gz
MongoDb 4.4 https://www.mongodb.com/try/download/community
Spring Boot 2.2.6.RELEASE https://spring.io/projects/spring-boot#learn
Spring Cloud Alibaba 2.2.1.RELEASE https://spring.io/projects/spring-cloud-alibaba/#learn
Nginx nginx-1.19.10 http://nginx.org/download/nginx-1.19.10.tar.gz
Nacos 1.3.1 https://github.com/alibaba/nacos/releases
Sentines-dashboard 1.3.0 https://github.com/alibaba/Sentinel/wiki

3.后端开发环境搭建

简易环境搭建流程:

1. 安装IDEA,JDK,Maven.mysql ,oracle等工具,并配置好相关的环境变量
2. 拉取项目导入到导入到idea中
3. 安装好项目需要使用的服务,redis,rabbitmq,nacos,Mognodb等环境
4. 进行项目编译启动。

3.1 Nacos 安装

微服务项目需要使用到注册中心和服务发现组件,我们必须要先安装启动好nacos-serve

下载地址:https://github.com/alibaba/nacos/releases

1.解压我们的 nacos

  1. cd 到nacos目录

  1. 使用 cd conf 进入conf路径下,修改配置, vim application.proproties 修改配置

数据库修改完成后 需要进行保存。 nacos默认是将数据持久化到本地文件,生产服务建议使用数据库配置。目前nacos只支持mysql数据库。注意如果是集群部署我们要确保每个服务都要链接 master节点。需要在数据库新建一个数据库 将 conf 目录下的 nacos-mysql.sql 导入到数据库

4.启动nacos cd bin 目录下,进入到bin目录下 执行命令(启动单机) sh startup.sh -m standalone

5.检查nacos启动的端口 lsof -i:8848

6.浏览器输入:http://192.168.163.129:8848/nacos 默认账号密码 : nacos/nacos

7.停止nocas 在nocas/bin目录下 执行 sh shutdown.sh

3.1.1 Window版本

其他操作修改配置同理 linux步骤 ,修改成功后 ,启动nacos,进入 bin 目录下,单机启动可以将startup.cmd文件的启动模式修改为单机模式:

保存 ,直接运行 startup.cmd 启动nacos

3.2 :Nacos client客户端的搭建

  1. 三板斧之一,依赖引入

    <!--注册中心客户端-->
    <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  2. 三板斧之二: 第二板斧写注解(也可以不写) @EnableDiscoveryClient

    @SpringBootApplication
    @EnableDiscoveryClient
    public class CloudAdminApplication {public static void main(String[] args) {SpringApplication.run(CloudAdminApplication.class, args);}}
    
  3. 三板斧之三 :写配置文件 注意server-addr:不需要写协议

spring:application:name: @artifactId@cloud:nacos:config:## 服务地址server-addr: 127.0.0.1:8848## 命名空间 nacos的领域模型,可以设置不同的空间 进行配置隔离,NameSpace (默认的NameSpace是”public“ NameSpace可以进行资源隔离,比如我们dev环境下的NameSpace下的服务是调用不到prod的NameSpace下的微服务。我们可以配置好不同开发环境的配置,上线的时候只要切换对应的 NameSpace即可namespace: 4c203b07-7179-4712-90c1-f0710ef11f5c
  1. 启动 cloud-admin-biz 项目

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b5pnszmo-1621556997409)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210429142551163.png)]

3.3 Nacos 领域模型划分

NameSpace(默认的NameSpace是”public“ NameSpace可以进行资源隔离,比如我们dev环境下的NameSpace下的服务是调用不到prod的NameSpace下的微服务)
比如:  cloud-admin-biz 的配置文件中配置了注册中心namespace 地址:只会注册到对应的 namespace
server:port: 7020
spring:application:name: cloud-admin-bizcloud:nacos:discovery:namespace: 4c203b07-7179-4712-90c1-f0710ef11f5c  命名空间与管理页面中的一致server-addr: 127.0.0.1:8848

3.4 配置中心 动态刷新

随着服务越来越多,配置参数也越来越多。有些配置是公共的,无需在每个配置文件中配置。为了统一管理配置,和动态的修改一个业务参数。在码云使用配置中心之前,维护起来都会很复杂。

微服务接入配置中心的步骤:

1.添加依赖包spring-cloud-alibaba-nacos-config

 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring‐cloud‐alibaba‐nacos‐config</artifactId></dependency>

2.编写配置文件,需要写一个bootstrap.yml配置文件 (

[^]:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kQDjIhXM-1621556997415)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210429155248807.png)])

server-addr: 127.0.0.1:8848  表示我微服务怎么去找我的配置中心
spring.application.name=cloud-admin-biz  表示当前微服务需要向配置中心索要cloud-admin-biz
的配置
spring.profiles.active=dev 表示我需要向配置中心索要cloud-admin-biz的生产环境的配置索要文件的格式为(data-id):${application.name}- ${spring.profiles.active}.${file-extension}真正在nacos配置中心上 就是    cloud-admin-biz-dev.yml
shared-configs: 共享配置    用来配置公共属性的 必须每个项目需要连接redis ,mysql 我们可以吧这两部分做一个公共的文件可以进行全局使用 refresh-enabled :支持动态刷新配置如下:
server:
port: 7020spring:
application:name: cloud-admin-biz
cloud:nacos:config:server-addr: 127.0.0.1:8848file-extension: ymlshared-configs:- application-dev.ymlnamespace: 4c203b07-7179-4712-90c1-f0710ef11f5crefresh-enabled: truediscovery:namespace: 4c203b07-7179-4712-90c1-f0710ef11f5cserver-addr: 127.0.0.1:8848
profiles:active: dev

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CnK1HD61-1621556997419)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210429155126435.png)]

启动项目,nacos 会自动的从配置中心去获取配置信息,本地就无需在进行配置了。

  1. 动态刷新

    1. 在我的cloud-admin-biz-dev.yml 配置文件中加入配置:edwin:text: yulang 2.在项目中需要编写:@RestController
    @RequestMapping("/index")
    @RefreshScope   //这个必须要写 动态刷新的注解
    public class DemoController {@Value("${edwin.text}")private String userName;@GetMapping("/test")public Object test(){return userName;}
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kak9jmFV-1621556997420)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210429162614172.png)]

直接在配置文件中修改:

edwin:text: yulang11111111

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QpGdLLyi-1621556997422)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210429162723485.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XsvsmcdP-1621556997423)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210429162751710.png)]

4.项目目录

D:.
├─cloud    项目父目录
├─cloud-admin  admin 平台服务
│  ├─cloud-admin-api 平台api
│  │  ├─src
│  │  │  └─main
│  │  │      └─java
│  │  │          └─com
│  │  │              └─cloud
│  │  │                  └─admin
│  │  │                      └─api
│  │  │                          ├─dto
│  │  │                          ├─entity
│  │  │                          ├─feign
│  │  │                          └─vo
│  └─cloud-admin-biz 平台业务服务
│      ├─src
│      │  ├─main
│      │  │  ├─java
│      │  │  │  └─com
│      │  │  │      └─cloud
│      │  │  │          └─admin
│      │  │  │              ├─config
│      │  │  │              ├─controller
│      │  │  │              ├─core
│      │  │  │              ├─handler
│      │  │  │              ├─mapper
│      │  │  │              └─service
│      │  │  │                  └─impl
│      │  │  └─resources 资源信息
│      │  │      ├─mapper
│      │  │      └─tenant
│      │  └─test  测试目录
│      │      └─java
│      │          └─com
│      │              └─cloud
│      │                  └─admin
├─cloud-auth  鉴权服务
│  ├─src
│  │  └─main
│  │      ├─java
│  │      │  └─com
│  │      │      └─cloud
│  │      │          └─auth
│  │      │              ├─config
│  │      │              ├─endpoint
│  │      │              ├─handler
│  │      │              └─service
│  │      └─resources 资源文件
│  │          ├─static
│  │          │  └─css
│  │          └─templates 页面
│  │              └─ftl
├─cloud-common 项目公共服务
│  ├─cloud-common-bom 公共依赖
│  ├─cloud-common-core 核心依赖 提供了一些高级的java类库,Google Guava 和 hutool工具
│  │  ├─src
│  │  │  └─main
│  │  │      ├─java
│  │  │      │  └─com
│  │  │      │      └─cloud
│  │  │      │          └─common
│  │  │      │              └─core
│  │  │      │                  ├─config
│  │  │      │                  ├─constant
│  │  │      │                  │  └─enums
│  │  │      │                  ├─exception
│  │  │      │                  ├─jackson
│  │  │      │                  ├─sensitive
│  │  │      │                  └─util
│  │  │      └─resources
│  │  │          ├─i18n
│  │  │          └─META-INF
│  ├─cloud-common-data 公共处理数据相关
│  │  ├─src
│  │  │  └─main
│  │  │      ├─java
│  │  │      │  └─com
│  │  │      │      └─cloud
│  │  │      │          └─common
│  │  │      │              └─data
│  │  │      │                  ├─cache
│  │  │      │                  ├─datascope
│  │  │      │                  ├─enums
│  │  │      │                  ├─handler
│  │  │      │                  ├─mybatis
│  │  │      │                  ├─quene
│  │  │      │                  ├─resolver
│  │  │      │                  └─tenant
│  │  │      └─resources
│  │  │          └─META-INF
│  ├─cloud-common-datasource 数据源相关配置
│  │  └─src
│  │      └─main
│  │          ├─java
│  │          │  └─com
│  │          │      └─cloud
│  │          │          └─common
│  │          │              └─datasource
│  │          │                  ├─annotation
│  │          │                  ├─config
│  │          │                  └─support
│  │          └─resources
│  │              └─META-INF
│  ├─cloud-common-gateway 网关相关配置
│  │  ├─src
│  │  │  └─main
│  │  │      └─java
│  │  │          └─com
│  │  │              └─cloud
│  │  │                  └─common
│  │  │                      └─gateway
│  │  │                          ├─annotation
│  │  │                          ├─configuration
│  │  │                          ├─filter
│  │  │                          ├─rule
│  │  │                          ├─support
│  │  │                          └─vo
│  ├─cloud-common-log 日志相关配置
│  │  ├─src
│  │  │  └─main
│  │  │      ├─java
│  │  │      │  └─com
│  │  │      │      └─cloud
│  │  │      │          └─common
│  │  │      │              └─log
│  │  │      │                  ├─annotation
│  │  │      │                  ├─aspect
│  │  │      │                  ├─event
│  │  │      │                  ├─init
│  │  │      │                  └─util
│  │  │      └─resources
│  │  │          └─META-INF
│  ├─cloud-common-minio  文件服务相关
│  │  ├─src
│  │  │  └─main
│  │  │      ├─java
│  │  │      │  └─com
│  │  │      │      └─cloud
│  │  │      │          └─common
│  │  │      │              └─minio
│  │  │      │                  ├─http
│  │  │      │                  ├─service
│  │  │      │                  └─vo
│  │  │      └─resources
│  │  │          └─META-INF
│  ├─cloud-common-security 安全认证相关
│  │  ├─src
│  │  │  └─main
│  │  │      ├─java
│  │  │      │  └─com
│  │  │      │      └─cloud
│  │  │      │          └─common
│  │  │      │              └─security
│  │  │      │                  ├─annotation
│  │  │      │                  ├─component
│  │  │      │                  ├─exception
│  │  │      │                  ├─feign
│  │  │      │                  ├─handler
│  │  │      │                  ├─listener
│  │  │      │                  ├─mobile
│  │  │      │                  ├─service
│  │  │      │                  ├─social
│  │  │      │                  └─util
│  │  │      └─resources
│  │  │          ├─META-INF
│  │  │          └─org
│  │  │              └─springframework
│  │  │                  └─security
│  ├─cloud-common-sentinel 限流熔断相关
│  │  ├─src
│  │  │  └─main
│  │  │      ├─java
│  │  │      │  └─com
│  │  │      │      └─cloud
│  │  │      │          └─common
│  │  │      │              └─sentinel
│  │  │      │                  ├─handle
│  │  │      │                  └─parser
│  │  │      └─resources
│  │  │          └─META-INF
│  ├─cloud-common-sequence 分布式主键相关
│  │  └─src
│  │      └─main
│  │          ├─java
│  │          │  └─com
│  │          │      └─cloud
│  │          │          └─common
│  │          │              └─sequence
│  │          │                  ├─builder
│  │          │                  ├─exception
│  │          │                  ├─properties
│  │          │                  ├─range
│  │          │                  │  └─impl
│  │          │                  │      ├─db
│  │          │                  │      ├─name
│  │          │                  │      └─redis
│  │          │                  └─sequence
│  │          │                      └─impl
│  │          └─resources
│  │              └─META-INF
│  └─cloud-common-swagger api接口文档
│      ├─src
│      │  └─main
│      │      └─java
│      │          └─com
│      │              └─cloud
│      │                  └─common
│      │                      └─swagger
│      │                          ├─annotation
│      │                          └─config├─cloud-gateway  网关服务,添加了动态路由功能
│  ├─src
│  │  └─main
│  │      ├─java
│  │      │  └─com
│  │      │      └─cloud
│  │      │          └─gateway
│  │      │              ├─config
│  │      │              ├─filter
│  │      │              └─handler
│  │      └─resources

4.1 cloud-admin

cloud-admin 项目为系统中的平台服务,主要分为 cloud-admin-api 和 cloud-admin-biz两个项目组成。cloud-admin-api: 主要包含一些工程中的实体类(po,vo),服务接口 提供对外暴露的api cloud-admin-biz: 主要实现平台的核心业务逻辑,服务接口的实现类,api接口地址服务等

1.cloud-admin-api pom文件结构

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"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><parent><groupId>com.cloud</groupId><artifactId>cloud-admin</artifactId><version>1.0.0</version></parent><artifactId>cloud-admin-api</artifactId><packaging>jar</packaging>只需要依赖核心公共组件<dependencies><!--core 工具类--><dependency><groupId>com.cloud</groupId><artifactId>cloud-common-core</artifactId></dependency></dependencies>
</project>

1.cloud-admin-biz pom文件结构 需要使用的对应功能组件 可以引入 cloud-common包下的对应的jar

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"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><parent><groupId>com.cloud</groupId><artifactId>cloud-admin</artifactId><version>1.0.0</version></parent><artifactId>cloud-admin-biz</artifactId><packaging>jar</packaging><description>通用用户权限管理系统业务处理模块</description><dependencies><!--upms api、model 模块--><dependency><groupId>com.cloud</groupId><artifactId>cloud-admin-api</artifactId></dependency><!--日志处理--><dependency><groupId>com.cloud</groupId><artifactId>cloud-common-log</artifactId></dependency><!--swagger api 相关配置--><dependency><groupId>com.cloud</groupId><artifactId>cloud-common-swagger</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><!--mybatis--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency><!--数据库--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--web 模块--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--undertow容器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>io.fabric8</groupId><artifactId>docker-maven-plugin</artifactId></plugin></plugins></build></project>
  1. 配置文件中配置好注册中心地址:

    server:port: 7020   ## 服务端口号spring:application:name: cloud-admin-biz  ## 应用名称 cloud:nacos:discovery:namespace: 4c203b07-7179-4712-90c1-f0710ef11f5cserver-addr: 127.0.0.1:8848config:server-addr: 127.0.0.1:8848   ## nacos 服务地址file-extension: yml  ## 配置文件扩展名shared-configs:  ## 共享资源配置- application-dev.ymlnamespace: 4c203b07-7179-4712-90c1-f0710ef11f5c ## 命名空间profiles:active: dev  # profiles 环境
    
    1. 启动 cloud-admin-biz 和网关项目

      访问接口文档:http://127.0.0.1:7000/doc.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-suthhpcq-1621556997425)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210429163156946.png)]

4.1.1 swagger组件使用:

  1. 项目中需要使用swagger的只需要在对应的项目中引入 :

    <!--swagger api 相关配置-->
    <dependency><groupId>com.cloud</groupId><artifactId>cloud-common-swagger</artifactId>
    </dependency>无需任何的配置文件 直接在项目中使用原生swagger注解即可使用
    
  2. 在项目中的 controller中编写 swaager注解:

    package com.cloud.admin.controller;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.cloud.admin.api.dto.UserDTO;
    import com.cloud.admin.api.dto.UserInfo;
    import com.cloud.admin.api.entity.SysUser;
    import com.cloud.admin.service.SysUserService;
    import com.cloud.common.core.util.R;
    import com.cloud.common.log.annotation.SysLog;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiImplicitParam;
    import io.swagger.annotations.ApiOperation;
    import lombok.AllArgsConstructor;
    import org.springframework.web.bind.annotation.*;import javax.validation.Valid;@RestController
    @AllArgsConstructor
    @RequestMapping("/user")
    @Api(value = "user", tags = "用户管理模块")
    public class SysUserController {private final SysUserService userService;/*** 获取指定用户全部信息** @return 用户信息*/@GetMapping("/info/{username}")@ApiOperation(value = "获取指定用户全部信息")@ApiImplicitParam(name = "username", value = "用户名", paramType = "String", dataTypeClass = UserInfo.class, required = true)public R info(@PathVariable("username") String username) {SysUser user = userService.getOne(Wrappers.<SysUser>query().lambda().eq(SysUser::getUsername, username));if (user == null) {return R.failed(null, String.format("用户信息为空 %s", username));}return R.ok(userService.findUserInfo(user));}/*** 通过ID查询用户信息** @param id ID* @return 用户信息*/@GetMapping("/{id}")@ApiOperation(value = "通过ID查询用户信息")@ApiImplicitParam(name = "id", value = "用户ID", paramType = "String", required = true)public R user(@PathVariable Integer id) {return R.ok(userService.selectUserVoById(id));}/*** 根据用户名查询用户信息** @param username 用户名* @return*/@GetMapping("/details/{username}")@ApiOperation(value = "根据用户名查询用户信息")@ApiImplicitParam(name = "username", value = "用户名", paramType = "String", required = true)public R user(@PathVariable String username) {SysUser condition = new SysUser();condition.setUsername(username);return R.ok(userService.getOne(new QueryWrapper<>(condition)));}/*** 删除用户信息** @param id ID* @return R*/@SysLog("删除用户信息")@DeleteMapping("/{id}")@ApiOperation(value = "删除用户", notes = "根据ID删除用户")@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "int", paramType = "path")public R userDel(@PathVariable Integer id) {SysUser sysUser = userService.getById(id);return R.ok(userService.deleteUserById(sysUser));}/*** 添加用户** @param userDto 用户信息* @return success/false*/@SysLog("添加用户")@PostMapping@ApiOperation(value = "删除用户", notes = "根据ID删除用户")public R user(@RequestBody UserDTO userDto) {return R.ok(userService.saveUser(userDto));}/*** 更新用户信息** @param userDto 用户信息* @return R*/@SysLog("更新用户信息")@PutMapping@ApiOperation(value = "更新用户信息", notes = "根据ID删除用户")public R updateUser(@Valid @RequestBody UserDTO userDto) {return R.ok(userService.updateUser(userDto));}/*** 分页查询用户** @param page    参数集* @param userDTO 查询参数列表* @return 用户集合*/@GetMapping("/page")@ApiOperation(value = "分页查询用户", notes = "分页查询用户")public R getUserPage(Page page, UserDTO userDTO) {return R.ok(userService.getUsersWithRolePage(page, userDTO));}/*** 修改个人信息** @param userDto userDto* @return success/false*/@SysLog("修改个人信息")@PutMapping("/edit")@ApiOperation(value = "修改个人信息", notes = "修改个人信息")public R updateUserInfo(@Valid @RequestBody UserDTO userDto) {return userService.updateUserInfo(userDto);}/*** @param username 用户名称* @return 上级部门用户列表*/@GetMapping("/ancestor/{username}")@ApiOperation(value = "根据用户名称查询上级部门用户列表", notes = "根据用户名称查询上级部门用户列表")public R listAncestorUsers(@PathVariable String username) {return R.ok(userService.listAncestorUsers(username));}
    }

4.1.2 cloud-common-core 使用

  1. 引入相对应依赖包:

    <!--core 工具类-->
    <dependency><groupId>com.cloud</groupId><artifactId>cloud-common-core</artifactId>
    </dependency>java 核心的工具类库 ,可以直接使用操作 java中集合,素组,等数据结构,以及反射,json,序列化,参数校验等功能

4.1.3 cloud-common-data 使用

  1. 引入相对应依赖包
<!--core 工具类-->
<dependency><groupId>com.cloud</groupId><artifactId>cloud-common-data</artifactId>
</dependency>

项目中封装好了,redisTemplate模板,和redis缓存的工具类。如果业务系统使用到reids缓存 可以直接集成

4.1.4 cloud-common-log 使用

1.引入相对应依赖包

<dependency><groupId>com.cloud</groupId><artifactId>cloud-common-log </artifactId>
</dependency>

1.日志依赖包,引入后,在应用系统中 配置日志模板 logback-spring.xml 配置到具体的项目resources目录下,系统会自动对controller层的日志加入trackId,方便在日志文件中排除错误;

  1. 提供了操作日志的入库操作,需要在使用的方法上 加上 @SysLog 注解,系统会自动将自动会对操作方法的日志进行入库操作。

4.1.5 cloud-common-minio (分布式文件存储)

1.引入相对应依赖包 (需要在需要使用文件上传存储业务中引入)

<dependency><groupId>com.cloud</groupId><artifactId>cloud-common-minio </artifactId>
</dependency>
  1. 启动 minio服务器

    需要先下载好服务端程序:https://dl.min.io/server/minio/release/windows-amd64/minio.exe启动服务:minio.exe server D:\        ## D:\  你启动的一些配置文件生成目录 可以自己制定
    

    启动完成后:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BqfphFYp-1621556997426)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210430103922844.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8z8oWmP8-1621556997428)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210430104019901.png)]

  1. 浏览器 输入 http://127.0.0.1:9000

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P6BT1fAv-1621556997429)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210430104135690.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LzMA7BQS-1621556997432)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210430104319563.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uHVRzvGw-1621556997433)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210430104752895.png)]

4.引入依赖后需要在业务系统中配置

 minio.url = http://127.0.0.1:9000 ## minio 服务地址 http://ip:portminio.accessKey =minioadmin   ## minio 用户名minio.secretKey =minioadmin   ##minio 密码minio.endpoint.enable =true ## 开启服务
  1. 代码中使用:

    @Autowired
    private MinioTemplate minioTemplate;public void uploadImage(File file){// bucketName bucket名称 可以理解为对应的目录名称// objectName 你的文件名称//InputStream stream 对应文件的二进制流minioTemplate.putObject("/TEST","12",new FileInputStream(file));
    }
    
    MinioTemplate 中封装了 如下方法 可以直接调用:/*** 创建bucket** @param bucketName bucket名称*/@SneakyThrowspublic void createBucket(String bucketName) {if (!client.bucketExists(bucketName)) {client.makeBucket(bucketName);}}/*** 获取全部bucket* <p>* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets*/@SneakyThrowspublic List<Bucket> getAllBuckets() {return client.listBuckets();}/*** @param bucketName bucket名称*/@SneakyThrowspublic Optional<Bucket> getBucket(String bucketName) {return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();}/*** @param bucketName bucket名称*/@SneakyThrowspublic void removeBucket(String bucketName) {client.removeBucket(bucketName);}/*** 根据文件前置查询文件** @param bucketName bucket名称* @param prefix     前缀* @param recursive  是否递归查询* @return MinioItem 列表*/@SneakyThrowspublic List<MinioItem> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {List<MinioItem> objectList = new ArrayList<>();Iterable<Result<Item>> objectsIterator = client.listObjects(bucketName, prefix, recursive);for (Result<Item> itemResult : objectsIterator) {objectList.add(new MinioItem(itemResult.get()));}return objectList;}/*** 获取文件外链** @param bucketName bucket名称* @param objectName 文件名称* @param expires    过期时间 <=7* @return url*/@SneakyThrowspublic String getObjectURL(String bucketName, String objectName, Integer expires) {return client.presignedGetObject(bucketName, objectName, expires);}/*** 获取文件** @param bucketName bucket名称* @param objectName 文件名称* @return 二进制流*/@SneakyThrowspublic InputStream getObject(String bucketName, String objectName) {return client.getObject(bucketName, objectName);}/*** 上传文件** @param bucketName bucket名称* @param objectName 文件名称* @param stream     文件流* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject*/public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {client.putObject(bucketName, objectName, stream, (long) stream.available(), null, null, "application/octet-stream");}/*** 上传文件** @param bucketName  bucket名称* @param objectName  文件名称* @param stream      文件流* @param size        大小* @param contextType 类型* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject*/public void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {client.putObject(bucketName, objectName, stream, size, null, null, contextType);}/*** 获取文件信息** @param bucketName bucket名称* @param objectName 文件名称* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject*/public ObjectStat getObjectInfo(String bucketName, String objectName) throws Exception {return client.statObject(bucketName, objectName);}/*** 删除文件** @param bucketName bucket名称* @param objectName 文件名称* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject*/public void removeObject(String bucketName, String objectName) throws Exception {client.removeObject(bucketName, objectName);}

4.1.6 cloud-common-sequence 分布式序列组件

1.依赖引入

<dependency><groupId>com.cloud</groupId><artifactId>cloud-common-sequence</artifactId>
</dependency>

2.功能介绍

插件包中提供2中生成分布式序列化的规则:
1: SnowflakeSequence  基于雪花算法
2: RangeSequence  基于号段 生成的有序递增的id其中号段中 也可以支持两种拨号器 a:基于数据库 b:基于redis

3.使用,在对应的配置 目前一个只系统支持一种生成方式

1.SnowflakeSequence开启方式:cloud:xsequence:snowflake:enable: true ##开启 snowflake算法datacenterId: 1  ##数据中心ID,值的范围在[0,31]之间,一般可以设置机房的IDC[必选]  每个系统禁止使用一样的配置 默认值为 1workerId: 1 ## 工作机器ID,值的范围在[0,31]之间,一般可以设置机器编号[必选]   每个系统禁止使用一样的配置 默认值为 1注意:如果使用 snowflake 方式,一定要注意时钟回拨的问题。避免造成id重复代码中使用:@Autowiredprivate Sequence sequence;@GetMapping("/test")public Object test() {return sequence.nextValue();}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQUz1nNg-1621556997435)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210430160127382.png)]

  1. 基于DB号段模式:

    cloud:xsequence:range:enable: db   ## 开启号段模式 db 方式 严格区分大小写tableName:  tb_range    ## 表名称 默认 tb_range 表 retryTimes: 1     ## 重试次数 默认1次step: 1000   ## 获取range步长[可选,默认:1000]stepStart: 0 ## 序列号分配起始值[可选:默认:0]bizName: cloud ## 业务名称  每个业务对应的名称 禁止中文
    ### 数据源的而配置 可以使用默认的
    spring:datasource:hikari:minimum-idle: 1maximum-pool-size: 3connection-timeout: 30000initialization-fail-timeout: 30000idle-timeout: 30000max-lifetime: 30000connection-test-query: SELECT 1type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456url: jdbc:mysql://127.0.0.1:3306/cloud-admin?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true      第一次启动会在对应的数据库中创建一张表 :
    CREATE TABLE `tb_range` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`value` bigint(20) NOT NULL,`name` varchar(32) NOT NULL,`gmt_create` datetime NOT NULL,`gmt_modified` datetime NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `uk_name` (`name`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;第一次使用会生成一条区间数据:
    1   1000    cloud   2021-04-30 15:36:56 2021-04-30 15:37:42注意在使用的时候 不要手动去修改表中数据,避免序列重复。
    

    代码中使用:

          @Autowiredprivate Sequence sequence;@GetMapping("/test")public Object test() {return sequence.nextValue();}
    
    1. 基于redis号段模式:

      cloud:xsequence:range:enable: redis   ## 开启号段模式 redis 方式 严格区分大小写  如果项目中已经引入了redis 则只需开启即可##redis配置可以使用默认        spring:redis:host: 192.168.163.130port: 6379password: adminstep:1000  ##获取range步长[可选,默认:1000]stepStart: 0  ##序列号分配起始值[可选:默认:0]bizName: cloud  ##业务名称  建议每个项目配置不同的代码中使用:@Autowiredprivate Sequence sequence;@GetMapping("/test")public Object test() {return sequence.nextValue();}   reids中会存储当前 一轮的步长,主要如果项目重启的话,第一轮步长没有使用完,回自动进入一个步长。必须第一次步长 1000,项目中id只是用到了 500,项目重启了,id会从 1001开始redis 中步长key :前缀 + 业务名 (x_sequence_ + 业务名 )
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P1CSLgWn-1621556997437)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210430162557467.png)]

4.1.7 cloud-common-sentinel(容灾限流)

1.依赖引入 (https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D)

<dependency><groupId>com.cloud</groupId><artifactId>cloud-common-sentinel</artifactId>
</dependency>

2.启动服务 ,配置服务监控地址

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar  也可以直接: java -jar sentinel-dashboard-1.7.0.jarspring:
cloud:sentinel:transport:dashboard: 127.0.0.1:8080
  1. sentinel 控制台配置项:

    配置项 类型 默认值 最小值 描述
    auth.enabled boolean true - 是否开启登录鉴权,仅用于日常测试,生产上不建议关闭
    sentinel.dashboard.auth.username String sentinel - 登录控制台的用户名,默认为 sentinel
    sentinel.dashboard.auth.password String sentinel - 登录控制台的密码,默认为 sentinel
    sentinel.dashboard.app.hideAppNoMachineMillis Integer 0 60000 是否隐藏无健康节点的应用,距离最近一次主机心跳时间的毫秒数,默认关闭
    csp.sentinel.dashboard.server String localhost:8080 指定控制台地址和端口

4.拦截详情日志

无论触发了限流、熔断降级还是系统保护,它们的秒级拦截详情日志都在 ${user_home}/logs/csp/sentinel-block.log里。如果没有发生拦截,则该日志不会出现。日志格式如下:

2014-06-20 16:35:10|1|sayHello(java.lang.String,long),FlowException,default,origin|61,0
2014-06-20 16:35:11|1|sayHello(java.lang.String,long),FlowException,default,origin|1,0

日志含义:

index 例子 说明
1 2014-06-20 16:35:10 时间戳
2 1 该秒发生的第一个资源
3 sayHello(java.lang.String,long) 资源名称
4 XXXException 拦截的原因, 通常 FlowException 代表是被限流规则拦截,DegradeException 则表示被降级,SystemBlockException 则表示被系统保护拦截
5 default 生效规则的调用来源(参数限流中代表生效的参数)
6 origin 被拦截资源的调用者,可以为空
7 61,0 61 被拦截的数量,0无意义可忽略

秒级监控日志

所有的资源都会产生秒级日志,它在 ${user_home}/logs/csp/${app_name}-${pid}-metrics.log里。格式如下:

1532415661000|2018-07-24 15:01:01|sayHello(java.lang.String)|12|3|4|2|295
  1. 1532415661000:时间戳
  2. 2018-07-24 15:01:01:格式化之后的时间戳
  3. sayHello(java.lang.String):资源名
  4. 12:表示到来的数量,即此刻通过 Sentinel 规则 check 的数量(passed QPS)
  5. 3:实际该资源被拦截的数量(blocked QPS)
  6. 4:每秒结束的资源个数(完成调用),包括正常结束和异常结束的情况(exit QPS)
  7. 2:异常的数量
  8. 295:资源的平均响应时间(RT)

业务日志

其它的日志在 ${user_home}/logs/csp/sentinel-record.log.xxx 里。该日志包含规则的推送、接收、处理等记录,排查问题的时候会非常有帮助。

集群限流日志

  • ${log_dir}/sentinel-cluster-client.log:Token Client 日志,会记录请求失败的信息

访问:http://127.0.0.1:8080/ (用户名/密码: sentinel /sentinel)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5L54tBER-1621556997438)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506110436001.png)]

  1. sentinel监控性能指标详解

    • 实时监控面板

      在这个面板中我们可以实时监控我们接口的**通过QPS拒绝的QPS**

      由于这没有配置拒绝的QPS,看到的都是正常的通过的QPS指标

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mVy2UqHJ-1621556997441)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506111100402.png)]

      设置流控规则为 2:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4tYMY4NR-1621556997442)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506111521918.png)]

    ​ 当QPS大于 2 接口返回:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPgXeSCz-1621556997443)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506111535755.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mtGDeR1b-1621556997445)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506111346852.png)]

    • 簇点链路 用来显示 服务锁监控的api

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vp6Cx04S-1621556997448)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506111916800.png)]

    • 流控设置

      簇点链路 点击具体访问的api 然后点击 流控 按钮

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZIQ51cYn-1621556997450)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506113005337.png)]

    含义:

    资源名称:为我们接口的API /index/test
    **针对来源:**这里是默认的default(标示不针对来源),还有一种情况就是
    假设微服务A需要调用这个资源,微服务B也需要调用这个资源,那么我们就可以单独的为微服务A和微服务B进行设置阈值。
    阈值类型: 分为QPS和线程数 假设阈值为2
    **QPS类型:**只得是每秒钟访问接口的次数>2就进行限流
    **线程数:**为接受请求该资源 分配的线程数>2就进行限流.[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XXkKyP1d-1621556997451)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506114453078.png)]

    流控模式

    1. ​ 直接 : 这种很好理解,就是达到设置的阈值后直接被流控抛出异常
      疯狂的请求这个路径, 当达到 阈值的时候会报错

    2. ​ 关联 :业务场景 我们现在有二个api,第一个是 查询接口,假设我们希望优先操
      作是保存接口

          /***:模仿 流控模式【关联】 读接口* @author: 余浪* @Date: 2021/3/10 14:33* @Version: 1.0.0**/@GetMapping("/test")@Inner(value =false)public Object test() {return userName + sequence.nextValue() +" >>> " +sequence.nextValue() +"------------";}/*** 模仿流控模式【关联】 写接口(优先)* @author: 余浪* @Date: 2021/3/10 14:33* @Version: 1.0.0**/@GetMapping("/save")@Inner(value =false)public Object save() {return "success";}测试代码: 写一个for循环一直调用我们的写接口,让写接口QPS达到阈值@GetMapping("/batch")@Inner(value = false)public void batch() throws InterruptedException {RestTemplate restTemplate =new RestTemplate();for (int i = 0; i < 1000; i++) {restTemplate.getForEntity("http://localhost:7020/index/save", String.class);Thread.sleep(200);}}
      

      ​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sOWPXEyr-1621556997452)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506115440921.png)]

      先访问:http://localhost:7020/index/batch,

      在访问 http://localhost:7020/index/test, 被限流了

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sSEODgXc-1621556997454)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506135120394.png)]

    3. 链路 (用法说明,本地实验没成功 用alibaba 未毕业版本0.9.0可以测试出效果 API级
      别的限制流量)

      ​ 测试代码

      编写一个service类:
      public interface SysLogService extends IService<SysLog> {int selectCount();
      }@Service
      public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements SysLogService {/***  SentinelResource 定义流控资源/@Override@SentinelResource("message")public int selectCount() {return 1;}
      }controller中分别定义2个接口调用/*** 测试链路 流控* @author: 余浪* @Date: 2021/3/10 14:33* @Version: 1.0.0**/
      @GetMapping("/findAll")
      public Object findAll(){int count = sysLogService.selectCount();return count;
      }/*** 测试链路 流控* @author: 余浪* @Date: 2021/3/10 14:33* @Version: 1.0.0**/
      @GetMapping("/findOne")
      public Object findOne(){int count = sysLogService.selectCount();return count;
      }

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EpkqkJvw-1621556997456)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506142704985.png)]

      • 根据流控规则来说: 只会限制/findAll的请求,不会限制/findOne规则 (目前我们版本 链路规则 使用issue 里面的方案未测试通过,官网视乎也有这样的问题,已经提issue)

      1.6.3 版本开始,Sentinel Web filter默认收敛所有URL的入口context,因此链路限流不生效。
      1.7.0 版本开始(对应SCA的2.1.1.RELEASE),官方在CommonFilter 引入了
      WEB_CONTEXT_UNIFY 参数,用于控制是否收敛context。将其配置为 false 即可根据不同的URL 进行链路限流。
      SCA 2.1.1.RELEASE之后的版本,可以通过配置spring.cloud.sentinel.web-context-unify=false即可关闭收敛
      我们当前使用的版本是SpringCloud Alibaba 2.1.0.RELEASE,无法实现链路限流。
      目前官方还未发布SCA 2.1.2.RELEASE,所以我们只能使用2.1.1.RELEASE,需要写代码的形式实

    4. 流控效果:

      ​ ①:快速失败(直接抛出异常) 每秒的QPS 操过1 就直接抛出异常
      源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

      ​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X9fwGhCT-1621556997457)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506172731744.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJ42XC2j-1621556997460)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506172744270.png)]

      ②:预热(warmUp)
      当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系
      统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到
      达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
      冷加载因子: codeFactor 默认是3
      默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SI2DZzZm-1621556997463)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506173127277.png)]

      上图设置: 就是QPS从3/3 = 1开始算 经过10秒钟,到达3 的QPS 才进行限制流量
      详情文档:https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—
      %E5%86%B7%E5%90%AF%E5%8A%A8
      源码:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

      ③:排队等待
      这种方式适合用于请求以突刺状来到,这个时候我们不希望一下子把所有的请求都通过,这样可能会
      把系统压垮;同时我们也期待系统以稳定的速度,逐步处理这些请求,以起到“削峰填谷”的效果,
      而不是拒绝所有请求。
      选择排队等待的阈值类型必须是QPS

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jO4qMDt7-1621556997464)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506173316929.png)]

      单机阈值: 10表示 每秒通过的请求个数是10,那么每隔100ms通过一次请求.
      每次请求的最大等待时间为1000=1s,超过1S就丢弃请求。
      具体文档:
      https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6-
      %E5%8C%80%E9%80%9F%E6%8E%92%E9%98%9F%E6%A8%A1%E5%BC%8F
      具体源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

    5. 降级规则
      ①:rt(平局响应时间)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rDs6fuSH-1621556997468)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507081626398.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RQzURt4R-1621556997469)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507081611919.png)]

      平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒
      级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中
      的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛
      出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作
      4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置

      ②:异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中
      的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s
      为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% -
      100%。

        测试代码: int a =1;@GetMapping("/ex")public String ex(){a++;if(a > 3){throw new RuntimeException("非法运算");}return sequence.nextNo();}
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2cq65yI5-1621556997472)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507083314169.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eRoyEslf-1621556997475)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507083302024.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-trubRUet-1621556997477)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507083419969.png)]③:异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后
      会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再
      进入熔断状态。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GvaCgSFL-1621556997478)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507083615131.png)]

    6. 热点参数:
      业务场景: 秒杀业务,比如商城做促销秒杀,针对苹果11(商品id=5)进行9.9秒杀活动,那么这个时候,我们去请
      求订单接口(商品id=5)的请求流量十分大,我们就可以通过热点参数规则来控制
      商品id=5的请求的并发量。而其他正常商品的请求不会收到限制。那么
      这种热点参数规则很使用 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ZCq0uua-1621556997479)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507084754043.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-846UuyXS-1621556997480)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507084946172.png)]

      ​ 热点规则的高级使用,修改刚刚新建的热点规则,配置 单独参数的QPS,id=22的阈值超过5限流,其他的id超过1限流

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i8ba0d4T-1621556997482)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507085516184.png)]

    7. 授权规则

      很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:

      若配置白名单,则只有请求来源位于白名单内时才可通过;

      若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Frczno08-1621556997483)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507090910793.png)]

      上面的资源名和授权类型不难理解,但是流控应用怎么填写呢?

      其实这个位置要填写的是来源标识,Sentinel提供了RequestOriginParser 接口来处理来源。只要Sentinel保护的接口资源被访问,Sentinel就会调用RequestOriginParser 的实现类去解析访问来源。

      1)自定义来源处理规则

      package com.cloud.common.sentinel.parser;import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;import javax.servlet.http.HttpServletRequest;/*** sentinel 请求头解析判断*/
      public class CloudHeaderRequestOriginParser implements RequestOriginParser {/*** Parse the origin from given HTTP request.** @param request HTTP request* @return parsed origin*/@Overridepublic String parseOrigin(HttpServletRequest request) {String serviceName = request.getParameter("serviceName");return serviceName;}
      }

      授权规则配置

      这个配置的意思是只有serviceName=pc不能访问(黑名单)

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YBLodKz3-1621556997484)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507091214409.png)]

    8. OpenFeign整合我们的Sentinel

      1.在对应的feign-api项目中

      com.cloud cloud-common-sentinel

      2.:在我们的Feign的声明式接口上添加fallback属性或者 fallbackFactory属

      1)为我们添加fallbackFactory属性的api ,fallbackFactory属性可以处理我们的异常,建议统一使用fallbackFactory

       package com.cloud.admin.api.feign;import com.cloud.admin.api.entity.SysDeptRelation;
      import com.cloud.admin.api.entity.SysRole;
      import com.cloud.admin.api.fallbackFactory.RemoteDataScopeServiceFallBackFactory;
      import com.cloud.common.core.constant.ServiceNameConstants;
      import com.cloud.common.core.util.R;
      import org.springframework.cloud.openfeign.FeignClient;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PathVariable;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RequestBody;import java.util.List;/*** 远程数据权限调用接口*/
      @FeignClient(contextId = "remoteDataScopeService", value = ServiceNameConstants.ADMIN_SERVICE,fallbackFactory = RemoteDataScopeServiceFallBackFactory.class)
      public interface RemoteDataScopeService {/*** 通过角色ID 查询角色列表** @param roleIdList 角色ID* @return*/@PostMapping("/role/getRoleList")R<List<SysRole>> getRoleList(@RequestBody List<String> roleIdList);/*** 获取子级部门** @param deptId 部门ID* @return*/@GetMapping("/dept/getDescendantList/{deptId}")R<List<SysDeptRelation>> getDescendantList(@PathVariable("deptId") Integer deptId);
      }package com.cloud.admin.api.fallbackFactory;import com.cloud.admin.api.entity.SysDeptRelation;
      import com.cloud.admin.api.entity.SysRole;
      import com.cloud.admin.api.feign.RemoteDataScopeService;
      import com.cloud.common.core.util.R;
      import feign.hystrix.FallbackFactory;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.stereotype.Component;import java.util.ArrayList;
      import java.util.List;/*** @author :yulang*  降级服务,抛出异常 给返回默认值。防止服务雪崩* @Date :  9:35* @Version : 1.0.0**/
      @Component
      @Slf4j
      public class RemoteDataScopeServiceFallBackFactory implements FallbackFactory<RemoteDataScopeService> {@Overridepublic RemoteDataScopeService create(Throwable throwable) {return new RemoteDataScopeService() {/*** 通过角色ID 查询角色列表*  可以做一些 其他的邮件通知等服务如果通过fegin调用getRoleList 异常,会直接返回默认值* @param roleIdList 角色ID* @return*/@Overridepublic R<List<SysRole>> getRoleList(List<String> roleIdList) {log.error("getRoleList error :{}",throwable);return R.ok(new ArrayList<SysRole>());}/*** 获取子级部门** @param deptId 部门ID* @return*/@Overridepublic R<List<SysDeptRelation>> getDescendantList(Integer deptId) {log.error("getDescendantList error :{}",throwable);return R.ok(new ArrayList<SysDeptRelation>());}};}
      }

      3.开启sentinel对fegin的支持

      feign:sentinel:enabled: true
      
    9. ​ ```

    10. Sentinel 规则持久化
      我们经过第四节课知道我们的Sentinel-dashboard配置的规则,在我们的微服
      务以及控制台重启的时候就清空了,因为他是基于内存的. 4

      推送模式 说明 优点 缺点
      原始模式 API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource 简单,无任何依赖 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境
      Pull 模式 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 简单,无任何依赖;规则持久化 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。
      Push 模式 扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 规则持久化;一致性;快速 引入第三方依赖

      ​ 原始模式

      如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:

      这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。

      Pull模式

      pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。以本地文件数据源为例:

      public class FileDataSourceInit implements InitFunc {@Overridepublic void init() throws Exception {String flowRulePath = "xxx";ReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));// 将可读数据源注册至 FlowRuleManager.FlowRuleManager.register2Property(ds.getProperty());WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerFlowDataSource(wds);}private <T> String encodeJson(T t) {return JSON.toJSONString(t);}
      }
      

      本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:

      首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull 模式的数据源时一般不需要对 Sentinel 控制台进行改造。

      这种实现方法好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性。

      Push模式

      生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:

      我们提供了 ZooKeeper, Apollo, Nacos 等的动态数据源实现。以 ZooKeeper 为例子,如果要使用第三方的配置中心作为配置管理,您需要做下面的几件事情:

      1. 实现一个公共的 ZooKeeper 客户端用于推送规则,在 Sentinel 控制台配置项中需要指定 ZooKeeper 的地址,启动时即创建 ZooKeeper Client。
      2. 我们需要针对每个应用(appName),每种规则设置不同的 path(可随时修改);或者约定大于配置(如 path 的模式统一为 /sentinel_rules/{appName}/{ruleType},e.g. sentinel_rules/appA/flowRule)。
      3. 规则配置页需要进行相应的改造,直接针对应用维度进行规则配置;修改同个应用多个资源的规则时可以批量进行推送,也可以分别推送。Sentinel 控制台将规则缓存在内存中(如 InMemFlowRuleStore),可以对其进行改造使其支持应用维度的规则缓存(key 为 appName),每次添加/修改/删除规则都先更新内存中的规则缓存,然后需要推送的时候从规则缓存中获取全量规则,然后通过上面实现的 Client 将规则推送到 ZooKeeper 即可。
      4. 应用客户端需要注册对应的读数据源以监听变更,可以参考 相关文档。

      从 Sentinel 1.4.0 开始,Sentinel 控制台提供 DynamicRulePublisherDynamicRuleProvider 接口用于实现应用维度的规则推送和拉取,并提供了相关的示例。Sentinel 提供应用维度规则推送的示例页面(/v2/flow),用户改造控制台对接配置中心后可直接通过 v2 页面推送规则至配置中心。改造详情可参考 应用维度规则推送示例。

      部署多个控制台实例时,通常需要将规则存至 DB 中,规则变更后同步向配置中心推送规则。

      PULL改造方案
      微服务改造方案:

      package com.cloud.common.sentinel.core;import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
      import com.alibaba.csp.sentinel.datasource.*;
      import com.alibaba.csp.sentinel.init.InitFunc;
      import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
      import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
      import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
      import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
      import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
      import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
      import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
      import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
      import com.alibaba.csp.sentinel.slots.system.SystemRule;
      import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
      import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
      import com.alibaba.csp.sentinel.util.ConfigUtil;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.InitializingBean;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.EnvironmentAware;
      import org.springframework.core.env.Environment;
      import org.springframework.stereotype.Component;import java.io.File;
      import java.io.FileNotFoundException;
      import java.nio.charset.Charset;
      import java.text.SimpleDateFormat;
      import java.util.Date;
      import java.util.List;
      import java.util.Properties;/***  sentinel 持久化* @author: 余浪* @Date: 2021/3/10 14:33* @Version: 1.0.0**/
      @Slf4j
      @Component
      public class PullModeByFileDataSource implements InitFunc {private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");@Overridepublic void init() throws Exception {log.info("time:{}读取配置",sdf.format(new Date()));try {Properties p = ConfigUtil.loadProperties("classpath:bootstrap.yml");String applicationName = p.getProperty("name","defalut");PersistenceRuleConstant.init(applicationName);//创建文件存储目录RuleFileUtils.mkdirIfNotExits(PersistenceRuleConstant.storePath,applicationName);//创建规则文件RuleFileUtils.createFileIfNotExits(PersistenceRuleConstant.rulesMap);dealFlowRules();dealDegradeRules();dealSystemRules();dealParamFlowRules();dealAuthRules();}catch (Exception e) {log.error("错误原因:{}",e);}}/*** 方法实现说明:处理流控规则逻辑* @author:smlz* @return: void* @exception: FileNotFoundException* @date:2019/11/29 13:26*/private void dealFlowRules() throws FileNotFoundException {String ruleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.FLOW_RULE_PATH).toString();//创建流控规则的可读数据源/*        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource(ruleFilePath,RuleListParserUtils.flowRuleListParser);*/ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource(new File(ruleFilePath),RuleListParserUtils.flowRuleListParser,20000,1024 * 1024, Charset.forName("utf-8"));// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存FlowRuleManager.register2Property(flowRuleRDS.getProperty());WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<List<FlowRule>>(ruleFilePath, RuleListParserUtils.flowFuleEnCoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);}/*** 方法实现说明:处理降级规则* @author:smlz* @return:void* @exception: FileNotFoundException* @date:2019/11/29 13:42*/private void dealDegradeRules() throws FileNotFoundException {//讲解规则文件路径String degradeRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.DEGRAGE_RULE_PATH).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource(degradeRuleFilePath,RuleListParserUtils.degradeRuleListParse);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRuleFilePath, RuleListParserUtils.degradeRuleEnCoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);}/*** 方法实现说明:处理系统规则* @author:smlz* @return:void* @exception: FileNotFoundException* @date:2019/11/29 13:42*/private void dealSystemRules() throws FileNotFoundException {//讲解规则文件路径String systemRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.SYSTEM_RULE_PATH).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource(systemRuleFilePath,RuleListParserUtils.sysRuleListParse);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存SystemRuleManager.register2Property(systemRuleRDS.getProperty());WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(systemRuleFilePath, RuleListParserUtils.sysRuleEnCoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);}/*** 方法实现说明:热点参数规则* @author:smlz* @return:* @exception:* @date:2019/11/29 13:50*/private void dealParamFlowRules() throws FileNotFoundException {//讲解规则文件路径String paramFlowRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.HOT_PARAM_RULE).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource(paramFlowRuleFilePath,RuleListParserUtils.paramFlowRuleListParse);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRuleFilePath, RuleListParserUtils.paramRuleEnCoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);}/*** 方法实现说明:授权规则* @author:smlz* @return:* @exception:* @date:2019/11/29 13:56*/private void dealAuthRules() throws FileNotFoundException {//讲解规则文件路径String authFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.AUTH_RULE_PATH).toString();//创建流控规则的可读数据源ReadableDataSource<String, List<AuthorityRule>> authRuleRDS = new FileRefreshableDataSource(authFilePath,RuleListParserUtils.authorityRuleParse);// 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存AuthorityRuleManager.register2Property(authRuleRDS.getProperty());WritableDataSource<List<AuthorityRule>> authRuleWDS = new FileWritableDataSource<>(authFilePath, RuleListParserUtils.authorityEncoding);// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSourceRegistry.registerAuthorityDataSource(authRuleWDS);}
      }
      

      基于SPI配置:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lFYtTYlj-1621556997490)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210506172145575.png)]

      启动项目这样 配置的流控规则,在项目启动后 就不会失效。

4.1.8 cloud-gateway

​ 网关服务,主要使用做同一路由转发,和动态路由(新增加一个服务不需要重启网关服务),灰度发布等功能。网关工程已经统一配置好,直接拉取代码直接使用即可

1.基于 gateway+ nacos 的动态路由规则配置,依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-gateway-core</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!--gateway 网关依赖,内置webflux 依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</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>

本项目采用的是基于配置文件开关配置

#nacos动态路由配置
nacos:gateway:route:config:data-id: gateway-routes  ##动态路由规则idgroup: DEFAULT_GROUP  ## 分组信息dynamicRoute:#是否开启动态网关路由enabled: true   ## true表示开启动态路由,false 默认按照网关配置文件配置

动态路由配置的代码实现

/*** 动态路由配置*/
@Configuration
public class GatewayConfig {public static final long DEFAULT_TIMEOUT = 30000;public static String NACOS_SERVER_ADDR;public static String NACOS_NAMESPACE;public static String NACOS_ROUTE_DATA_ID;public static String NACOS_ROUTE_GROUP;@Value("${spring.cloud.nacos.discovery.server-addr}")public void setNacosServerAddr(String nacosServerAddr){NACOS_SERVER_ADDR = nacosServerAddr;}@Value("${spring.cloud.nacos.discovery.namespace}")public void setNacosNamespace(String nacosNamespace){NACOS_NAMESPACE = nacosNamespace;}@Value("${nacos.gateway.route.config.data-id}")public void setNacosRouteDataId(String nacosRouteDataId){NACOS_ROUTE_DATA_ID = nacosRouteDataId;}@Value("${nacos.gateway.route.config.group}")public void setNacosRouteGroup(String nacosRouteGroup){NACOS_ROUTE_GROUP = nacosRouteGroup;}}package com.cloud.gateway.route;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;/*** 动态更新路由网关service* 1)实现一个Spring提供的事件推送接口ApplicationEventPublisherAware* 2)提供动态路由的基础方法,可通过获取bean操作该类的方法。该类提供新增路由、更新路由、删除路由,然后实现发布的功能。*/
@Slf4j
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;/*** 发布事件*/@Autowiredprivate ApplicationEventPublisher publisher;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.publisher = applicationEventPublisher;}/*** 删除路由* @param id* @return*/public String delete(String id) {try {log.info("gateway delete route id {}",id);this.routeDefinitionWriter.delete(Mono.just(id));return "delete success";} catch (Exception e) {return "delete fail";}}/*** 更新路由* @param definition* @return*/public String update(RouteDefinition definition) {try {log.info("gateway update route {}",definition);this.routeDefinitionWriter.delete(Mono.just(definition.getId()));} catch (Exception e) {return "update fail,not find route  routeId: "+definition.getId();}try {routeDefinitionWriter.save(Mono.just(definition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));return "success";} catch (Exception e) {return "update route fail";}}/*** 增加路由* @param definition* @return*/public String add(RouteDefinition definition) {log.info("gateway add route {}",definition);routeDefinitionWriter.save(Mono.just(definition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));return "success";}
}package com.cloud.gateway.route;import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.cloud.gateway.config.dynamic.GatewayConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;/**
* 通过nacos下发动态路由配置,监听Nacos中gateway-route配置
*
*/
@Component
@Slf4j
@ConditionalOnProperty(prefix = "nacos.gateway.dynamicRoute", name = "enabled", havingValue = "true")
@DependsOn({"gatewayConfig"}) // 依赖于gatewayConfig bean
public class DynamicRouteServiceImplByNacos {@Autowiredprivate DynamicRouteServiceImpl dynamicRouteService;private ConfigService configService;@PostConstructpublic void init() {log.info("初始化网关路由。。。");try{configService = initConfigService();if(configService == null){log.warn("initConfigService fail");return;}String configInfo = configService.getConfig(GatewayConfig.NACOS_ROUTE_DATA_ID, GatewayConfig.NACOS_ROUTE_GROUP, GatewayConfig.DEFAULT_TIMEOUT);if(StrUtil.isNotBlank(configInfo)) {log.info("获取网关当前配置:\r\n{}", configInfo);List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);for (RouteDefinition definition : definitionList) {log.info("update route : {}", definition.toString());dynamicRouteService.add(definition);}}else{log.error("没有从nacos获取到网关路由信息,请检查!");}} catch (Exception e) {log.error("初始化网关路由时发生错误",e);}dynamicRouteByNacosListener(GatewayConfig.NACOS_ROUTE_DATA_ID,GatewayConfig.NACOS_ROUTE_GROUP);}/*** nacos config  监听Nacos下发的动态路由配置* @param dataId* @param group*/public void dynamicRouteByNacosListener (String dataId, String group){try {configService.addListener(dataId, group, new Listener()  {@Overridepublic void receiveConfigInfo(String configInfo) {log.info("进行网关更新:\n\r{}",configInfo);List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);for(RouteDefinition definition : definitionList){log.info("update route : {}",definition.toString());dynamicRouteService.update(definition);}}@Overridepublic Executor getExecutor() {log.info("getExecutor\n\r");return null;}});} catch (NacosException e) {log.error("从nacos接收动态路由配置出错!!!",e);}}/*** 初始化网关路由 nacos config* @return*/private ConfigService initConfigService(){try{Properties properties = new Properties();properties.setProperty("serverAddr", GatewayConfig.NACOS_SERVER_ADDR);properties.setProperty("namespace",GatewayConfig.NACOS_NAMESPACE);return configService= NacosFactory.createConfigService(properties);} catch (Exception e) {log.error("初始化网关路由时发生错误",e);return null;}}
}

网关中添加配置,只需要在nacos配置中心修改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dw7WYbxa-1621556997493)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507131938224.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cVz19NJw-1621556997495)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507132057323.png)]

点击发布 网关会自动刷新路由信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7f7VqoM4-1621556997496)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507132313748.png)]

我们也可以在nacos中的监听查询列表中查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MT1oiyY-1621556997497)(C:\Users\yulan\AppData\Roaming\Typora\typora-user-images\image-20210507132429228.png)]

4.1.9 nacos+gateway 实现的跨集群 同版本调用

我们现在需要解决生产环境金丝雀发布问题
比如 server-A 存在二个版本 V1(老版本) V2(新版
本),server-B 也存在二个版本V1(老版本) V2新版本 现在需要
做到的是
server-A(V1)---->server-B(v1),server-A(V2)—
->server-B(v2)。记住v2版本是小面积部署的,用来测试用
户对新版本功能的。若用户完全接受了v2。我们就可以把V1版本卸载
完全部署V2版本

@Slf4j(topic = "theSameClusterPriorityWithVersionRule")
public class GrayTheSameClusterPriorityWithVersionRule extends AbstractLoadBalancerRule {@Autowiredprivate NacosDiscoveryProperties nacosDiscoveryProperties;@Overridepublic void initWithNiwsConfig(IClientConfig iClientConfig) {}@Overridepublic Server choose(Object key) {try {//获取本地所部署集群的名称 NJ-CLUSTERString localClusterName = nacosDiscoveryProperties.getClusterName();//去nacos上获取和本地 相同集群   相同版本的所有实例信息List<Instance> theSameClusterNameAndTheSameVersionInstList = getTheSameClusterAndTheSameVersionInstances(nacosDiscoveryProperties);//声明被调用的实例Instance toBeChooseInstance;//判断同集群同版本号的微服务实例是否为空if (theSameClusterNameAndTheSameVersionInstList.isEmpty()) {//跨集群调用相同的版本toBeChooseInstance = crossClusterAndTheSameVersionInovke(nacosDiscoveryProperties);} else {//具有同集群  同版本号的实例toBeChooseInstance = CustomerWeightedBalancer.chooseInstanceByRandomWeight(theSameClusterNameAndTheSameVersionInstList);log.info("同集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",localClusterName, toBeChooseInstance.getClusterName(), nacosDiscoveryProperties.getMetadata().get("version"),toBeChooseInstance.getMetadata().get("version"), toBeChooseInstance.getIp(), toBeChooseInstance.getPort());}return new NacosServer(toBeChooseInstance);} catch (NacosException e) {log.error("同集群优先权重负载均衡算法选择异常:{}", e);return null;}}/*** 方法实现说明:获取相同集群下,相同版本的 所有实例** @param nacosDiscoveryProperties nacos的配置* @author:smlz* @return: List<Instance>* @exception: NacosException*/private List<Instance> getTheSameClusterAndTheSameVersionInstances(NacosDiscoveryProperties nacosDiscoveryProperties) throws NacosException {//当前的集群的名称String currentClusterName = nacosDiscoveryProperties.getClusterName();//当前的版本号String currentVersion = nacosDiscoveryProperties.getMetadata().get("version");//获取所有实例的信息(包括不同集群的,不同版本号的)List<Instance> allInstance = getAllInstances(nacosDiscoveryProperties);List<Instance> theSameClusterNameAndTheSameVersionInstList = new ArrayList<>();//过滤相同集群  同版本号的实例for (Instance instance : allInstance) {if (StringUtils.endsWithIgnoreCase(instance.getClusterName(), currentClusterName) &&StringUtils.endsWithIgnoreCase(instance.getMetadata().get("version"), currentVersion)) {theSameClusterNameAndTheSameVersionInstList.add(instance);}}return theSameClusterNameAndTheSameVersionInstList;}/*** 方法实现说明:获取被调用服务的所有实例** @param nacosDiscoveryProperties nacos的配置* @author:smlz* @return: List<Instance>* @exception: NacosException* @date:2019/11/21 16:42*/private List<Instance> getAllInstances(NacosDiscoveryProperties nacosDiscoveryProperties) throws NacosException {//第1步:获取一个负载均衡对象BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) getLoadBalancer();//第2步:获取当前调用的微服务的名称String invokedSerivceName = baseLoadBalancer.getName();//第3步:获取nacos clinet的服务注册发现组件的apiNamingService namingService = nacosDiscoveryProperties.namingServiceInstance();//第4步:获取所有的服务实例List<Instance> allInstance = namingService.getAllInstances(invokedSerivceName);return allInstance;}/*** 方法实现说明:跨集群环境下 相同版本的** @param nacosDiscoveryProperties* @author:smlz* @return: List<Instance>* @exception: NacosException* @date:2019/11/21 17:11*/private List<Instance> getCrossClusterAndTheSameVersionInstList(NacosDiscoveryProperties nacosDiscoveryProperties) throws NacosException {//版本号String currentVersion = nacosDiscoveryProperties.getMetadata().get("version");//被调用的所有实例List<Instance> allInstance = getAllInstances(nacosDiscoveryProperties);List<Instance> crossClusterAndTheSameVersionInstList = new ArrayList<>();//过滤相同版本for (Instance instance : allInstance) {if (StringUtils.endsWithIgnoreCase(instance.getMetadata().get("version"), currentVersion)) {crossClusterAndTheSameVersionInstList.add(instance);}}return crossClusterAndTheSameVersionInstList;}private Instance crossClusterAndTheSameVersionInovke(NacosDiscoveryProperties nacosDiscoveryProperties) throws NacosException {//获取所有集群下相同版本的实例信息List<Instance> crossClusterAndTheSameVersionInstList = getCrossClusterAndTheSameVersionInstList(nacosDiscoveryProperties);//当前微服务的版本号String currentVersion = nacosDiscoveryProperties.getMetadata().get("version");//当前微服务的集群名称String currentClusterName = nacosDiscoveryProperties.getClusterName();//声明被调用的实例Instance toBeChooseInstance = null;//没有对应相同版本的实例if (crossClusterAndTheSameVersionInstList.isEmpty()) {log.info("跨集群调用找不到对应合适的版本当前版本为:currentVersion:{}", currentVersion);throw new RuntimeException("找不到相同版本的微服务实例");} else {toBeChooseInstance = CustomerWeightedBalancer.chooseInstanceByRandomWeight(crossClusterAndTheSameVersionInstList);log.info("跨集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",currentClusterName, toBeChooseInstance.getClusterName(), nacosDiscoveryProperties.getMetadata().get("current-version"),toBeChooseInstance.getMetadata().get("current-version"), toBeChooseInstance.getIp(), toBeChooseInstance.getPort());}return toBeChooseInstance;}
}

4.1.10

springcloudalibaba项目使用情况相关推荐

  1. DSB2017项目grt123代码学习笔记一:项目基本情况

    DSB2017项目grt123代码学习笔记一:项目基本情况 Kaggle上Data Science Bowl 2017年肺结节检测比赛第一名grt123团队的算法. github地址:https:// ...

  2. 项目进展情况如何更好地管理?

    项目进度管理的主要目标是要在规定的时间内,制定出合理.经济的进度计划,然后在该计划的执行过程中,检查实际进度是否与计划进度相一致,保证项目按时完成. 智办事可以满足 包含任务协作.即时沟通.数据管理. ...

  3. 百度首席科学家吴恩达谈百度人工智能项目进展情况

    吴恩达(Andrew Ng)是人工智能领域的大牛之一.他是Google人工智能部门的其中一位创始人,是2012年Google通过自主学习识别猫图像的深度学习神经网络"DistBelief&q ...

  4. 37个项目!2020年5G+智慧灯杆项目落地情况大汇总

    春节将近,2020年已然成为历史.今天我们回顾2020年智慧灯杆的建设情况,一起看看具体都有哪些智慧路灯项目在2020年成功落地? 在本篇文章中,小编汇总了4个直辖市13个省份,共计37个项目.其中落 ...

  5. springcloudalibaba项目的搭建

    第一步:搭建父项目,创建一个Maven项目,父项目不写代码,直接删除src 第二步:父项目需要的包 // 打包方式 <packaging>pom</packaging> < ...

  6. 系统试运行报告是谁写的_费控系统项目实施情况说明

    费用控制系统项目为今年我司信息化建设重大项目,项目旨在对费用预算进行实时的系统管控和严格执行,促进财务管理和会计核算流程的标准化和精简化,同时为员工的费用报销.出差商旅等经济事项提供灵活高效的处理通道 ...

  7. Tomcat项目卡件情况:调整启动参数

    tomcat项目有卡件情况,调整线程数,调整内存 进入tomcat/bin 目录下,修改catalina.sh或catalina.out文件(根据自己系统情况) JAVA_OPTS="-se ...

  8. 电脑重启后,eclipse可能会出现项目丢失情况

    解决方法如下: 1.File-->Import-->Existing Projects into Workpace 选择项目所在的目录 2.勾选Search for nested proj ...

  9. linux环境下查看项目内存情况

    2019独角兽企业重金招聘Python工程师标准>>> 1. 查看内存占用 2.查看cpu占用 3.查看gc信息 转载于:https://my.oschina.net/zhaolin ...

最新文章

  1. 深度学习核心技术精讲100篇(四十二)-阿里妈妈深度树匹配技术演进:TDM->JTM->BSAT
  2. ccf-csp #201703-2 学生排队
  3. 关于编译GITHUB上的工程
  4. php 源文件加密工具PHP Screw
  5. 从淘宝第1位程序员, 到阿里合伙人,20多年了,非科班出身的他还在编程,程序员的榜样
  6. 2018.12.18
  7. ES6中新增数组遍历方法
  8. 锐捷 重启计算机,关于锐捷客户端重安装后要求反覆重启的解决办法
  9. android qq轻聊版,Android QQ轻聊版怎么样 Android QQ轻聊版介绍
  10. 电子邮件工作原理及主要协议
  11. C/C++ int数组初始化
  12. ToolBar的返回键以及对应的图标自定义设置
  13. iOS第三方开源库的吐槽和备忘 - 王培
  14. 声学模型(语音识别中的)--学习笔记
  15. 洗地扫地机一体机好用吗、洗扫一体洗地机选购必看
  16. 按键精灵使用百度文字识别(百度ocr)教程
  17. python文件操作和绘制曲线
  18. 小说作者推荐:焦糖冬瓜合集
  19. 【Java中继承总结与练习 】
  20. eclipse Luna 用tomct7 部署项目

热门文章

  1. 资讯:微火荣获首批“支付宝数字化经营合作伙伴认证”
  2. 虹科白皮书 | 在工业4.0阶段,如何利用TSN时间敏感网络技术打造数字化工厂?
  3. 高温计通用协议 Data format UPP(Universal Pyrometer Protocol)
  4. 【产业互联网周报】百度AI产业化框架初成;微信公开课强调toB;谷歌或收购Salesforce;“女全经济”主导全家消费。...
  5. 【NER】CCL2021医疗命名体识别之数据预处理(处理.json文件)
  6. 生成excel文档即便没有安装微软Excel
  7. vb.net 教程 10-2 Excel操作1 Excel文件的打开
  8. C++实现Wlan自动连接(wpa2 enterprise)
  9. 获取任意微信公众号二维码方法
  10. 互联网日报 | 3月11日 星期四 | 小米10S正式发布;汽车之家将于3月15日登陆港交所;达达快送年配送订单量达11亿单...