Spring Cloud Alibaba(全家桶)

Spring Cloud Alibaba

官网:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md

由于Netflix进入维护模式,阿里巴巴推出SpringCloud Alibaba一系列的框架,解决分布式微服务架构的问题。

版本对应关系:

Spring Cloud AlibabaSpring CloudSpring Boot
2022.0.0*Spring Cloud 2022.0.03.0.2
2022.0.0-RC2Spring Cloud 2022.0.03.0.2
2022.0.0-RC1Spring Cloud 2022.0.03.0.0
2021.0.5.0*Spring Cloud 2021.0.52.6.13
2021.0.4.0*Spring Cloud 2021.0.42.6.11
2021.0.1.0*Spring Cloud 2021.0.12.6.3
2021.1Spring Cloud 2020.0.12.4.2

服务注册和配置中心

Nacos

官网地址:https://nacos.io/zh-cn/index.html

下载地址:https://github.com/alibaba/Nacos

文档地址:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_nacos_discovery

启动nacos

# 解压安装包,然后在bin目录下的运行,单机启动
startup.cmd -m standalone
# 启动成功,运行
http://localhost:8848/nacos
用户名、密码:nacos
# window上关闭服务器
shutdown.cmd

# linux进行启动
# sh startup.sh -m standalone
# linux进行访问
curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'
# linux上关闭服务器
sh shutdown.sh

注册中心配置:

步骤1:引包:

<!--SpringCloud ailibaba nacos -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

步骤2:改配置文件

server:
  port: 80

spring:
  application:
    name: nacos-order
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

步骤3:启动服务

# 访问地址,会发现该服务已注册上nacos上
http://localhost:8848/nacos

nacos它是支持AP和CP模式,默认是AP

K8S服务和DNS服务则适用于CP模式、切换CP模式:

curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'

配置中心配置:

步骤1:引包:

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

步骤2:改配置文件

server:
  port: 80

spring:
  application:
    name: nacos-order
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848 # Nacos作为配置中心地址
        file-extension: yaml # 指定yaml格式的配置

步骤3:在nacos创建对应的配置文件

Data Id :服务名-版本号-文件后缀 例:nacos-order-dev-yaml
# 具体的配置文件在该文件里配置

步骤4:启动服务

# 启动服务,根据不同环境配置启动
java -Dspring.profiles.active=dev -jar xxx.jar
# 访问地址,会发现该服务已注册上nacos上,然后我们更改配置文件,对应的服务也刷新更改
http://localhost:8848/nacos
保护阈值

注册上的nacos的服务实例,默认都是临时实例,客户端会每隔5秒发送一次心跳检测,服务端如果没有收到客户端的心跳,会把该实例标记不健康的服务,如果30秒没有收到,就会删除该实例。

在使用中,保护阈值可以设置一个0-1的比例,例子:如果有100个服务:20健康服务/80个不健康服务;正常情况,我们只能访问到这20健康服务,但在高并发的请求下,一下访问到这20健康服务,会导致这20台健康服务挂掉,导致全部都不能访问,所有当我们设置保护阈值的值,这100个服务都打开,nginx的负载均衡会随机访问,肯能访问80不健康的服务,也有肯能访问到20健康的服务,把错误推给客户端。当我们保护阈值设置0.5,就是有百分20的概率访问。

在这里插入图片描述

命名空间多环境多状态配置

Namespace+Group+Data ID的关系

相当于java的包名、类名、方法,默认是Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT,我们可以自己配置,配置更加细节、不同环境、不同的场景、不同用户访问情况的配置文件。

历史版本回退

nacos还提供历史版本的回退,在配置管理下有历史版本,输入对应Data ID和Group,查询最近更改的记录

监控查询

在配置管理下有监控查询,它负责查询,该配置在哪台机器上使用

集群和持久化配置

单机配置

步骤1:数据库配置

# 创建数据库 (nacos_config)
CREATE DATABASE nacos_config;
USE nacos_config;
# 找到初始化脚本,在nacos下conf目录下找到nacos-mysql.sql,执行初始化SQL脚本

步骤2:更改配置文件

# 配置文件:在nacos下conf的application.properties文件(切换数据库)
# 文件里的内容:在最下面,添加-切换数据库配置内容
spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
db.user=root
db.password=数据库密码

# 也可以在该文件,进行修改端口
server.port=8848

步骤3:重启nacos服务

# 重启:startup.cmd -m standalone
# 这样我们在访问:http://localhost:8848/nacos
# 会是重新的配置,每次配置会保存到数据库上

Linux版Nacos集群配置

步骤1:下载对应的Linux版Nacos包,上传到服务器上,并解压到安装目录

步骤2:同单机配置一样,完成单机配置的步骤1、步骤2

步骤3:修改cluster.conf文件

# 我们配置3台Nacos机器的ip地址,
# 注意:一定是具体的IP地址,可通过该命令查看:hostname -i 能够识别的IP
192.168.11.64:3333
192.168.11.64:4444
192.168.11.64:5555
# 如果不是同一个ip地址,分别在不同的机器,改IP地址,省略第4步骤
# 启动,使用默认启动,./startup.sh  分别在其他机器上进行启动

步骤4:修改startup.sh文件

# 里面的内容要修改
while getopts ":m:f:s:p" opt
do
	case $opt in
		m)
			MODE=$OPTARG;;
		f)
			...
		s) 
			SERVER=$OPTARG;;
		p)  
			PORT=OPTARG;;
		?)
...
# start
echo "$JAVA ${JAVA_OPT} ...." > ...
nohup $JAVA -Dserver.port=${PORT} ${JAVA_OPT} nacos...
...
	

步骤5:修改nginx的配置

# 我们通过nginx进行负载均衡,对外访问nginx,然后nginx负载均衡到这3台Nacos服务
# nginx的负载均衡配置
upstream cluster{
    server 192.168.11.64:3333;
    server 192.168.11.64:4444;
    server 192.168.11.64:5555;
}
server {
    listen       8848;
    server_name  localhost;
    location / {
   		 proxy_pass http://cluster;
	}
	...
}

步骤6:启动ngin

# 在nginx的sbin目录下,启动nginx
./nginx -c /usr/nginx的配置文件路径地址

步骤7:启动Nacos

# 在Nacos的bin目录下,进行启动,这2台Nacos服务
./startup.sh -p 3333  # 启动其中一台
./startup.sh -p 4444
./startup.sh -p 5555
# 可通过Nacos页面,在集群管理这里,查看节点信息 

步骤8:更改我们项目中的配置

# 启动这些服务成功,我们访问nginx的地址,通过负载均衡配置转发到Nacos服务

# 然后我们项目中的,配置服务,连接nginx的地址
spring:
  application:
    name: xxx
  cloud:
    nacos:
      discovery:
        # 配置Nacos地址
        #server-addr: localhost:8848
        # 换成nginx的1111端口,做集群
        server-addr: 192.168.11.64:1111
        # 如果我们不使用nginx ,可以多个IP,逗号分隔,但官方建议使用nginx
        # server-addr: 192.168.11.64:3333,192.168.11.64:4444,192.168.11.64:5555

熔断与限流

Sentinel

它可以解决服务雪崩、服务降级、服务熔断、服务限流;

中文:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

下载:https://github.com/alibaba/Sentinel/releases

版本说明:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

运行命令:

# 启动
java -jar sentinel-dashboard-1.8.6.jar
# 访问sentinel管理界面,登录账号密码均为sentinel
http://localhost:8080

# 或者启动
# -Dproject.name:指定本服务的名称 -Dcsp.sentinel.dashboard.serve:指定sentinel控制台的地址
# -Dsentinel.dashboard.auth.username:登录用户名 -Dsentinel.dashboard.auth.password:密码
java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard  -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456 -jar sentinel-dashboard-1.8.6.jar

# 访问sentinel管理界面
http://localhost:8888

服务端需要注册上Sentinel

步骤1: 引包

<!--SpringCloud ailibaba nacos -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

步骤2:修改配置文件

spring:
  application:
    name: sentinel-service
  cloud:
    nacos:
      discovery:
        # Nacos服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        # 配置Sentinel dashboard地址
        dashboard: localhost:8080
        # 默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719

步骤3:启动服务

# 启动服务,并访问几个接口
# 登录sentinel管理界面
http://localhost:8080
实时监控

可监控该服务的接口,请求频率。响应时间等信息;
在这里插入图片描述

流控规则

解释字段

  • 资源名:唯一名称,默认请求路径
  • 针对来源:可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  • 阈值类型/单机值
    • QPS(每秒钟的请求数量):当调用该api就QPS达到阈值的时候,进行限流
    • 线程数.当调用该api的线程数达到阈值的时候,进行限流
  • 是否集群:不需要集群
  • 流控模式:
    • 直接:api达到限流条件时,直接限流。分为QPS和线程数
    • 关联:当关联的资到阈值时,就限流自己。别人惹事,自己买单
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源
  • 流控效果:
    • 快速失败:直接抛异常
    • warm up:根据codeFactor(冷加载因子,默认3)的值,从阈值codeFactor,经过预热时长,才达到设置的QPS阈值
    • 排队等待:匀速排毒,让请求以匀速通过,阈值类型必须设置为QPS,否则无效

例子:

请求该路径:http://xxxx/t1/testA,当它的QPS达到5的时候,就会直接失败,返回:Blocked by Sentinel (flow limiting)
在这里插入图片描述
注意:QPS和线程数的区别,就是QPS(每秒请求数),当每秒的请求数达到阈值就会报错;线程数代表,设置了3个线程,就3个工作线程,当大量请求,分别去找这3个工作线程,如果3个都在工作,没有空余线程,会报错。

流控模式

一. 直接(默认),直接就是当前的资源名。

二. 关联

当我们 请求接口:/testA  ,正常
当我们 请求接口:/testB  ,正常
当我们 大量的请求:/testB 接口 的时候,再去访问 http://localhost:8401/testA ,此时的testA会进入服务降级.
也就是,当请求testB,请求的阈值是设置的值,就会影响到testA的接口访问。

在这里插入图片描述

三 链路:

在这里插入图片描述

访问:/testC  - 单独访问 正常
访问:/testD  - 单独访问 正常
但如果:配置了链路,多次访问/testC正常,但/testD多次访问,会进入流控。
流控效果

一. 快速失败

直接抛出异常:抛出异常:Blocked by Sentinel (flow limiting)

二. 预热(WarmUp)

选择这个,需要设置预热时长,默认是3,它是经过一段时间才逐渐升至设定的 QPS 阈值
例子:阀值为10+预热时长设置5秒,它就是10/3 约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10
场景:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。

三. 排队等待

匀速排队:设置排队等待必须在阀值类型是QPS模式下,设置排队等待,可设置超时时间,单位毫秒。
它就是当QPS超过设置的阀值,就进行排队等待,后期会慢慢执行,如果超时了等待时间,就不在排队。
降级规则

官网:https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

一. RT(平均响应时间,秒级)

  • 平均响应时间超出阈值在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
  • 窗口期过后关闭断路器
  • RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

二. 异常比列(秒级)

  • QPS >= 5异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级,就是该接口出现异常,大于设置的异常比例,如:0.2,就是百分之20,并且QPS请求>=5,才会触发熔断

三. 异常数(分钟级)

  • 异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
    在这里插入图片描述
    Sentinel 的断路器是没有半开状态的,

半开状态就是,没有异常访问正常方法,有异常访问其他方法。

热点key限流

热点即经常访问的数据,比如某些参数是某个值,进行限流操作,其他参数不进行限流。

案例:

// 正常逻辑 - 执行
@GetMapping("/testD")
@SentinelResource(value = "testD", blockHandler = "dealTestHotKey")
public String testD(@RequestParam(value = "p1", required = false) String p1,
                    @RequestParam(value = "p2", required = false) String p2) {
    return "------testD -- 正常逻辑返回" ;
}

// 触发-降级的方法
public String dealTestHotKey(String p1, String p2, BlockException exception) {
    return "-----dealTestHotKey 服务降级方法";
}

在这里插入图片描述

案例:资源名,与@SentinelResource里的value对应,参数索引,代表第几个参数,图中是参数1,单机阈值是2,代表每秒QPS达到2个就会触发该限流方法。会执行代码中降级里的方法。

http://localhost:8401/t3/testD?p1=a        # 会触发
http://localhost:8401/t3/testD?p2=ss&p1=a  # 会触发
http://localhost:8401/t3/testD?p2=a        # 不会触发

在这里插入图片描述
参数例外项

当我们的参数值是24的时候,阈值是1,代表是24,QPS是1会触发限流;当参数值是25,阈值是5,QPS是5会触发限流里的方法逻辑。

http://localhost:8401/t3/testD?p2=ss&p1=24  # 连续点击2次,会触发
http://localhost:8401/t3/testD?p2=ss&p1=24  # 多次频繁点击,会触发
授权规则

在某种场景,可以根据调用接口来源判断是否允许执行本次请求,可以提供白名单与黑名单两种授权类型。

在这里插入图片描述

// 配置黑名单
// 访问:http://localhost:8401/r1/test6?serverName=t1  会被授权,限制
// 访问:http://localhost:8401/r1/test6?serverName=t2  会被授权,限制
// 访问:http://localhost:8401/r1/test6?serverName=t22 不会被限制
系统规则

这个是配置整体服务的,

  • Load自适应:需要在Linux上配置,这个是自适应系统保护,当系统的并发线程超过估算的系统容量会触发保护机制,系统容量计算:最大的QPS * 最小的Rt 估算出来,一般设置几核CPU*2.5
  • CPU,当系统的CPU超过设置的值会触发保护机制,取值0-1,如果是0.8,就是当CPU使用率超过80%会触发
  • 平均RT:当单台机器上所有的入口流量的平均RT达到阈值会触发,单位毫秒
  • 入口QPS:当单机上所有入口流量QPS达到阈值,就会触发

在这里插入图片描述

SentinelResource

方式1:

// 该注解,使用,既可以用url进行配置,也可以根据配置名称
@GetMapping("/testD")
@SentinelResource(value = "testD", blockHandler = "testHotKey")
public String testD(@RequestParam(value = "p1", required = false) String p1,
                    @RequestParam(value = "p2", required = false) String p2) {
    return "------testD -- 正常逻辑返回" ;
}
// 触发-降级的方法
public String testHotKey(String p1, String p2, BlockException exception) {
return "-----testD dealTestHotKey 服务降级方法";
}

方式2:

// 正常逻辑 - 执行 , 熔断限流,方法在该CustomerBlockHandler下的handleException2方法
@GetMapping("/testE")
@SentinelResource(value = "testE", 
                  blockHandlerClass = CustomerBlockHandler.class, 
                  blockHandler = "handleException2")
public String testD() {
    return "------testE -- 正常逻辑返回" ;
}

// CustomerBlockHandler类 (自定义限流处理逻辑)
public class CustomerBlockHandler {

    public static String handleException(BlockException exception) {
        return "自定义的限流处理信息......限流方法1";
    }

    public static String handleException2(BlockException exception) {
        return "自定义的限流处理信息......限流方法2";
    }
    
}
配置持久化

每次重启项目不再需要重新配置

步骤1:引包

 <!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
 <dependency>
     <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-datasource-nacos</artifactId>
 </dependency>

步骤2:修改配置文件

spring:
  application:
    name: sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        # 配置Sentinel dashboard地址
        dashboard: localhost:8888
        port: 8080
      # sentinel持久化
      datasource:
        dsl-flow:
          nacos:
            server-addr: localhost:8848
            # nacos 的 Data ID
            data-id: ${spring.application.name}
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow

当Gateway使用了流控配置的持久化,那么sentinel中datasource配置下的rule-type参数值就得是gw_flow, 不能是flow

如果配置的是flow,那么在sentinle中就不会显示处Gateway的流控配置,但是不影响功能。

如果配置的是gw_flow,才会在sentinel中显示Gateway的流控配置,同时也不影响功能。

步骤3:配置nacos文件

添加对应的文件,例子:

// 流控配置
[
    {
        "resource":"fallback",  // 资源名
        "limitApp":"default",   // 针对来源,若为 default 则不区分调用来源
        "grade":1,		        // 限流阈值类型(1:QPS;  0:并发线程数)
        "count":1,		        // 阈值
        "strategy":0,           // 流控模式(0:直接; 1:关联; 2:链路)
        "controlBehavior":0,    // 流控效果 (0:快速失败;  1:Warm Up(预热模式);  2:排队等待)
        "clusterMode":false,    // 是否是集群模式
        "warmUpPeriodSec": 10,  // 预热时间(秒,预热模式需要此参数)
        "maxQueueingTimeMs": 500,   // 超时时间(排队等待模式需要此参数)
        "refResource": "rrr"    // 关联资源、入口资源(关联、链路模式)
    }
]

// 熔断降级配置
[
    {
        "resource": "fallback",
        "grade": 0,    // 熔断策略,支持慢调用比例(0),异常比例(1),异常数(2)策略
        "count": 1000, // 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用,单位ms);异常比例/异常数模式下为对应的阈值
        "slowRatioThreshold": 0.1,  // 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
        "minRequestAmount":  10,    //熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断
        "timeWindow": 10,           // 熔断时长,单位为 s
        "statIntervalMs": 1000      // 统计时长(单位为 ms),如 60*1000 代表分钟级
    }
]	

// 热点参数配置
[
  {
    "resource": "/test1",
    "grade": 1,           // 限流模式(QPS 模式,不可更改)
    "paramIdx": 0,        // 参数索引
    "count": 13,          // 单机阈值
    "durationInSec": 6,   // 统计窗口时长
    "clusterMode": false, // 是否集群 默认false
    "controlBehavior": 0, // 流控效果(支持快速失败和匀速排队模式)
    "limitApp": "default",
    // 高级选项
    "paramFlowItemList": [{
        "classType": "int", // 参数类型
        "count": 222,       // 限流阈值
        "object": "2"       // 参数值
      }]
  }
]

// 系统规则配置
[
  {
    "avgRt": 1,            // RT
    "highestCpuUsage": -1, // CPU 使用率
    "highestSystemLoad": -1, // LOAD
    "maxThread": -1,         // 线程数
    "qps": -1,               // 入口 QPS
    "count": 55,             // 阈值,在CPU使用率中是百分比
  }
]

// 授权规则配置
[
  {
    "resource": "sentinel_spring_web_context",
    "limitApp": "/test",
    "strategy": 0   // 授权类型(0代表白名单;1代表黑名单。)
  }
]

在这里插入图片描述

分布式事务

Seata

是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

官网地址:https://seata.apache.org/zh-cn/

文档:https://seata.apache.org/zh-cn/docs/overview/what-is-seata/

下载:https://github.com/apache/incubator-seata/tags

术语

  • TC 事务协调者
  • TM 事务管理器
  • RM 资源管理器

分布式事务过程

  • Transaction ID XID:全局唯一的事务ID
  • 三大组件:Transaction Coordinator (TC) - 事务协调者、TM (Transaction Manager) - 事务管理器、RM (Resource Manager) - 资源管理器

过程:

  1. TM向TC申请开启一个全局事务,创建成功并生成一个全局唯一的XID;
  2. XID在微服务调用链路的上下文中传播
  3. RM向TC注册分支事务,将其纳入XID对应全局事务的管辖
  4. TM向TC发起针对XID的全局提交或回滚决议
  5. TC调度XID下管辖的全部分支事务完成提交或回滚要求

各事务模式

  • XA 模式 (强一致)

    第一阶段提交,执行SQL脚本,事务并没有提交;
    第二阶段提交,没有问题,进行事务的提交,否则进行回滚。
    缺点:第一阶段,需要对数据库资源进行锁定,到第二阶段,才进行释放资源,性能较差;
    优点:数据强一致
    
  • AT 模式 (弱一致)

    第一阶段提交,执行SQL脚本前,会对该表增加全局锁,进行锁定状态,记录快照(undo_log表)记录操作前后的数据记录,然后SQL执行完,提交事务,进行解锁;
    第二阶段提交,没有问题,进行删除快照(undo_log表)该条数据历史记录;有问题进行回滚,根据快照(undo_log表)进行恢复数据,再删除该表的快照记录,如果快照记录与现在实际数据不一致,需要抛出异常,人工处理;
    优点:第一阶段,事务就提交,释放资源,性能比XA模式好
    缺点:第二阶段前,是软状态,等第二阶段完成,是数据最终一致性,存在如果全脚事务和普通事务同时操作该行数据,避免不了事务的一致性问题。
    
  • TCC 模式(弱一致)

    第一阶段提交,执行SQL脚本,进入Try方法里,会记录表进行资源锁定,为冻结数据,并提交事务;
    第二阶段提交,没有问题,会进入 Confirm 方法里,删除冻结数据,失败的话,进入Cancel方法里,进行手动回滚数据;
    优点:跟AT模式一样,是性能最好的,不依赖数据库,
    缺点:有代码侵入,需要人为编写try、Confirm和Cancel接口,还需要做接口幂等性问题。
    
  • Saga 模式 (最终一致)

    第一阶段提交,执行sql脚本,提交事务;
    第二阶段提交,没有问题,不做处理,有问题,进行手动回滚,补偿数据;
    优点:它性能最高,适合长事务解决方案、或者第3方的接口;
    缺点,会出现脏数据、没有做到事务的隔离性;
    

安装

步骤1:在nacos控制台添加新的命名空间

命名空间:seata,并记录命名空间ID,后面要用。

步骤2:修改seata配置文件

修改seata/conf/application.yml文件

server:
  port: 7091

spring:
  application:
    # 注册上的服务名
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash

console:
  user:
    username: seata
    password: seata

seata:
  # 配置中心
  config:
    # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      # 命名空间id
      namespace: 6344c52c-ec84-49f0-b38b-bdce31aa5e68
      group: SEATA_GROUP
      username: nacos
      password: nacos
      # seataServer的配置文件
      data-id: seataServer.properties
      #context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:
  # 注册中心    
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos
    # preferred-networks: 30.240.*
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: DEFAULT_GROUP
      # 对应的命名空间,在nacos中配置,没有就空
      # namespace: 6344c52c-ec84-49f0-b38b-bdce31aa5e68
      # 指定注册至nacos注册中心的集群名,该服务部署在上海,就是SH,北京就是BJ,一个名字而已
      cluster: BJ
      username: nacos
      password: nacos
  store:
    # support: file 、 db 、 redis
    mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
      user: root
      password: 数据库密码
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 1000
      max-wait: 5000
#  server:
#    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login

步骤3:创建数据表

注意:不同版本的数据库不相同,需要去官网找对应的SQL脚本,脚本目录:xxx\seata\script\server\db里。

-- 在一个seata单独的数据库上创建
-- 全局事务
CREATE TABLE IF NOT EXISTS `global_table`
(
    `xid`                       VARCHAR(128) NOT NULL,
    `transaction_id`            BIGINT,
    `status`                    TINYINT      NOT NULL,
    `application_id`            VARCHAR(32),
    `transaction_service_group` VARCHAR(32),
    `transaction_name`          VARCHAR(128),
    `timeout`                   INT,
    `begin_time`                BIGINT,
    `application_data`          VARCHAR(2000),
    `gmt_create`                DATETIME,
    `gmt_modified`              DATETIME,
    PRIMARY KEY (`xid`),
    KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- 分支事务
CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- the table to store lock data 全局锁
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_status` (`status`),
    KEY `idx_branch_id` (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS `distributed_lock`
(
    `lock_key`       CHAR(20) NOT NULL,
    `lock_value`     VARCHAR(20) NOT NULL,
    `expire`         BIGINT,
    primary key (`lock_key`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

步骤4:下载服务端的Seata,进行修改配置文件

在nacos创建seataServer.properties的配置文件,如果上面配置了命名空间、分组需要保持一致;

把配置文件config.txt加载到nacos上,原文件在seata/script/config-center/config.txt

... 以下内容要改
#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
store.mode=db
store.lock.mode=file
store.session.mode=file
#Used for password encryption
... 数据库改成自己的seata
#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
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&characterEncoding=UTF-8&serverTimezone=GMT%2B8
store.db.user=root
store.db.password=数据库密码

步骤5:加载到nacos上

还需要在nacos上添加Data ID:service.vgroupMapping.default_tx_group配置文件,配置事务组:

分组:SEATA_GROUP、值:default

如果创建集群,可多创建多个配置配置,该端口号和分组配置等,客户端这边可以动态同步,就实现集群部署。

步骤6:回滚日志表,需要在每个微服务创建对应的表

DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

步骤7:启动

# 启动
# 双击运行/bin/seata-server.bat
# 启动成功后,会在nacos中看到seata-server服务(事务管理器服务)
# 然后登陆seata可视化界面:http://127.0.0.1:7091
# 默认用户名和密码:seata

步骤8:客户端配置

# 引包
<!--seata依赖需要排除掉,用了什么版本的seata就用那个版本的依赖-->
<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>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.6.1</version>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>1.6.1</version>
</dependency>

更改yml配置

# 分布式事务配置
seata:
  enabled: true
  # 该服务注入到seata的应用程序,需要唯一
  application-id: ${spring.application.name}
  # 事务的模式,默认是 AT模式 、XA模式  TCC魔术(需要注释掉)
  data-source-proxy-mode: AT
  # 事务组,根据这个获取tc服务端cluster名称,默认是 default_tx_group
  tx-service-group: yyybj
  service:
    vgroup-mapping:
      # key是事务分组名称 value要和服务端的机房名称保持一致,与cluster名称一致,还需要在nacos上配置
      yyybj: BJ
  # 注册中心
  registry:
    type: nacos
    nacos:
      # Seata服务名(应与seata-服务端的实际注册的服务名一致)
      application: seata-server
      server-addr: 127.0.0.1:8848
      # nacos 命令空间
      # namespace: 6344c52c-ec84-49f0-b38b-bdce31aa5e68
      group: DEFAULT_GROUP
  # 配置中心
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      # nacos 命令空间
      namespace: 6344c52c-ec84-49f0-b38b-bdce31aa5e68
      group: SEATA_GROUP

2.0版本实操案例

步骤1:创建数据表

注意:不同版本的数据库不相同,需要去官网找对应的SQL脚本,脚本目录:xxx\seata\script\server\db里。

global_table、branch_table、lock_table、distributed_lock // 这4张表

步骤2:更改seata的配置文件

目录:seata\2.0.0\conf\application.yml
server:
  port: 7091 
spring:
  application:
    name: seata-server
logging:
  config: classpath:logback-spring.xml
  file:
    path: ${log.home:${user.home}/logs/seata}
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash
# seata 客户端 登录用户名和密码 
console:
  user:
    username: seata
    password: seata
seata:
  # 配置中心
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace:
      group: SEATA_GROUP #后续自己在nacos里面新建,不想新建SEATA_GROUP,就写DEFAULT_GROUP
      username: nacos
      password: nacos
   # 注册中心
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP #后续自己在nacos里面新建,不想新建SEATA_GROUP,就写DEFAULT_GROUP
      namespace:
      cluster: default
      username: nacos
      password: nacos    
  # 全局事务的存在位置
  store:
    mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/seata?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
      user: 数据库用户名
      password: 数据库密码
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 1000
      max-wait: 5000
  #  server:
  #    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**

步骤3:启动nacos

startup.cmd -m standalone

步骤4:启动Seata

在目录:seata\2.0.0\bin
执行:seata-server.bat 命令

步骤5:验证

访问 nacos 客户端,seata是否注册成功
地址:http://localhost:8848/nacos
访问 seata 客户端
地址:http://localhost:7091/#/login

步骤6:准备3个测试数据库

create database seata_a;
create database seata_b;
create database seata_c;

// 回滚日志表,需要在每个微服务创建对应的库中,创建回滚日志表
// 但是AT模式下,需要创建,如果其他模式,不需要创建该表
```sql
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
```
--- 分别创建对应的自己的库中
CREATE TABLE `t_axx` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
  `total` int DEFAULT NULL COMMENT '总额度',
  `used` int DEFAULT NULL COMMENT '已用余额',
  `residue` int DEFAULT '0' COMMENT '还有金额',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 初始值,代表1号用户有100总金额,已使用0,还有100金额
INSERT INTO `t_axx` (`id`, `user_id`, `total`, `used`, `residue`) VALUES (1, 1, 1000, 0, 1000);

CREATE TABLE `t_bxx` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
  `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
  `count` int(11) DEFAULT NULL COMMENT '数量',
  `money` decimal(11,0) DEFAULT NULL COMMENT '金额',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `t_cxx` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
  `total` int(11) DEFAULT NULL COMMENT '总库存',
  `used` int(11) DEFAULT NULL COMMENT '已用库存',
  `residue` int(11) DEFAULT NULL COMMENT '剩余库存',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 初始值,代表1号产品有100库存,已用0,剩余100
INSERT INTO `t_cxx` (`id`,`product_id`,`total`, `used`, `residue`) VALUES (1, 1, 100, 0, 100);

步骤7:服务准备

// 需要创建3个微服务,具体代码已分享在码云上,Spring Cloud Alibaba系列。//
// 编写对应的代码,开启全局事务注解,在调用者处增加
@GlobalTransactional(name = "bxx-create", rollbackFor = Exception.class)

步骤8:登录Seata客户端

用户名密码默认都是Seata,可查看事务的信息,全局锁信息。

在这里插入图片描述

在这里插入图片描述

网关

Gateway

它为微服务架构提供有效的统一的API路由管理方式,底层使用了Netty通讯框架。可以做反向代理、鉴权、流量控制、熔断、日志监控、防止SQL注入、防止Web攻击、证书处理。

它与Zuul的区别:

Zuul是基于IO的API网关,基于Servlet 2. 5使用阻塞架构。Spring Cloud Gateway它是使用非阻塞API。

三大核心:

  • 路由:Route(如果断言正确,匹配该路由)
  • 断言:Predicate(请求与断言是否匹配)
  • 过滤:Filter(进行细化的控制)

创建新的服务当网关

步骤1:引包

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

步骤2:修改yml

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true   # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: cloud1_provider_routh          # 路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001       # 匹配后提供服务的路由地址(第一版,可以写固定的)
          uri: lb://cloud1-provider          # 匹配后提供服务的路由地址,与注册中心的服务名一致
          predicates:
            - Path=/t2/test1/**              # 断言,路径相匹配的进行路由
            
        - id: cloud1_pxx_routh
          uri: lb://cloud1-pxx
          predicates:
            - Path=/pxx/test1/**
            # 断言,当在这个时间之前可以使用该路由
            - After=2022-03-08T23:14:51.718+08:00[Asia/Shanghai]   
            # 断言,当在这个时间之后可以使用该路由
            - Before=2022-03-08T23:14:51.718+08:00[Asia/Shanghai]
            # 断言 在这个范围之内
            - Between=2022-02-02T17:45:06.206+08:00[Asia/Shanghai],2023-03-25T18:59:06.206+08:00[Asia/Shanghai] 
            # 断言,必须携带该Cookie,不带该Cookie会爆404
            # 携带Cookie,
            # 访问:curl http://localhost:9527/pxx/test1/xxxx --cookie “username=yan”
            - Cookie=username,yan       
           
            # 断言,请求头要有X-Request-Id属性并且值为整数的正则表达式,否则会404,
            # 请求 curl http://localhost:9527/pxx/xxx -H “X-Request-Id:-123”
            - Header=X-Request-Id, \d+  
            
            # 断言,请求域名,必须以goodyan.com结尾,  
            # 请求 curl http://localhost:9527/pxx/xxx -H “Host: java.goodyan.com”
            - Host=**.goodyan.com      
                       
            # 断言,必须是get请求           
            - Method=GET        
            # 断言,要有参数名username并且值还要是整数才能路由
            - Query=username, \d+       
          # 过滤器,路由过滤器只能指定路由进行使用,可用于修改进入的HTTP请求和返回的HTTP响应
          filters:
          	# 过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Id值为1024
            - AddRequestParameter=X-Request-Id,1024  

步骤3:进行测试

// 访问服务端机器1:http://localhost:8001/t2/test1/6
// 访问服务端机器2:http://localhost:8003/t2/test1/6
// 访问网关地址:   http://localhost:9527/t2/test1/6 
自定义断言
// 当满足自己设置的断言规则,才可以访问该服务
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
    public MyRoutePredicateFactory() {
        super(MyRoutePredicateFactory.Config.class);
    }
    // 这个Config类就是我们的路由断言规则,重要
    @Validated
    public static class Config {
        @Setter
        @Getter
        @NotEmpty
        private String userType; // yml配置的会员等级
    }
    // 如果不配置这个,需要yml这里,这么配置
    //  - name: My
    //      args:
    //        userType: diamond
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("userType");
    }
    @Override
    public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                // 检查request的参数里面,userType是否为指定的值,符合配置就通过
                // 访问:http://localhost:xxx/xx/get/1               接口  失败
                // 访问:http://localhost:xxx/xx/get/1?userType=xxx  接口  失败
                // 访问:http://localhost:xxx/xx/get/1?userType=gold 接口  成功
                String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
                if (userType == null) {
                    return false;
                }
                // 如果说参数存在,与config的数据进行比较,一致会放行,可以访问
                if (userType.equalsIgnoreCase(config.getUserType())) {
                    return true;
                }
                return false;
            }
        };
    }

}

自定义全局过滤器
// 给每个服务通过网关进行请求,增加耗时时间
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {

    //开始调用方法的时间
    public static final String BEGIN_VISIT_TIME = "begin_visit_time";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1 先记录下访问接口的开始时间
        exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
        // 2 返回统计的各个结果给后台
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
            if (beginVisitTime != null) {
                log.info("访问接口主机:" + exchange.getRequest().getURI().getHost());
                log.info("访问接口端口:" + exchange.getRequest().getURI().getPort());
                log.info("访问接口URL:" + exchange.getRequest().getURI().getPath());
                log.info("访问接口URL后面参数:" + exchange.getRequest().getURI().getRawQuery());
                log.info("访问接口时长" + (System.currentTimeMillis() - beginVisitTime) + "毫秒");
                log.info("============分割线==========================");
            }
        }));
    }

    // 数字越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}
自定义单一过滤器
// 给某些服务指定配置,需要特殊参数,才可以访问 
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
    public MyGatewayFilterFactory() {
        super(MyGatewayFilterFactory.Config.class);
    }
    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 得到请求头
                ServerHttpRequest request = exchange.getRequest();
                System.out.println("进入了自定义网关过滤器 - yml中配置的status:" + config.getStatus());
                // 如果参数yy,进行放行,没有返回 - 回应头
                if (request.getQueryParams().containsKey("yy")) {
                    return chain.filter(exchange);
                } else {
                    exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }
    // 如果不配置这个,需要yml这里,需要全写,参考 断言的
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("status");
    }
    public static class Config {
        @Getter
        @Setter
        private String status; //设定一个状态值/标志位,它等于多少,匹配和才可以访问
    }
}

对应的代码可在SpringCloud案例查看,分享在:gitee

  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值