微服务保护

目录

初识Sentinel

雪崩问题

认识Sentinel

安装Sentinel控制台

引入cloud-demo

微服务整合sentinel

限流规则

簇点链路

快速入门

流控模式

流控模式-关联

流控模式-链路

流控效果

[流控效果-warm up](#流控效果-warm up)

流控模式-排队等待

热点参数限流

隔离和降级

隔离和降级

Feign整合sentinel

线程隔离

熔断降级

熔断策略-慢调用

熔断策略-异常比例、异常数

授权规则及规则持久化

授权规则

自定义异常结果

规则管理模式

实现push模式


什么是雪崩问题?

1、微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况。
如何避免因瞬间高并发流量而导致服务故障?

1、流量控制
如何避免因服务故障引起的雪崩问题?

1、超时处理

2、线程隔离

3、降级熔断


流控模式有哪些?

1、直接:对当前资源限流

2、关联:高优先级资源触发阈值,对低优先级资源限流。

3、链路:阈值统计时,只统计从指定资源进入当前资源的请求,是对请求来源的限流


流控效果有哪些?

1、快速失败:QPS超过阈值时,拒绝新的请求

2、warm up:QPS超过阈值时,拒绝新的请求;QPS阈值是逐渐提升的,可以避免冷启动时高并发导致服务宕机。

3、排队等待:请求会进入队列,按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长大于超时时间,直接拒绝


Sentinel支持的雪崩解决方案:

1、线程隔离(仓壁模式)

2、降级熔断
Feign整合Sentinel的步骤:

1、在application.yml中配置:feign.sentienl.enable=true

2、给Feignclient编写FallbackFactory并注册为Bean

3、将FallbackFactory配置到FeignClient


线程隔离的两种手段是?

1、信号量隔离

2、线程池隔离
信号量隔离的特点是?

1、基于计数器模式,简单,开销小
线程池隔离的特点是?

1、基于线程池模式,有额外开销,但隔离控制更强


sentinel熔断降级的策略有哪些?

1、慢调用比例:超过指定时长的调用为慢调用,统计单位时长内慢调用的比例,超过阈值则熔断

2、异常比例:统计单位时长内异常调用的比例,超过阈值则熔断

3、常数:统计单位时长内异常调用的次数,超过阈值则熔断


获取请求来源的接口是什么?

1、Request0riginParser
处理BlockException的接口是什么?

1、BlockExceptionHandler


sentinel的三种配置管理模式是什么?

1、原始模式:保存在内存

2、pull模式:存在本地文件或数据库,定时去读取

3、push模式:保存在nacos,监听变更实时更新

初识Sentinel

雪崩问题

微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。

解决雪崩问题的常见方式有四种:

1、超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待2、舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离。

3、熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。

4、流量控制:限制业务访问的QPS,避免服务因流量的突增而故障。

认识Sentinel

Sentinel是阿里巴巴开源的一款微服务流量控制组件。

Sentinel具有以下特征:

1、丰富的应用场景:Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

2、完备的实时监控:Sentinel同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。

3、广泛的开源生态:Sentinel提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、Dubbo、gRPC的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。

4、完善的SPI扩展点:Sentinel提供简单易用、完善的SPI扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

安装Sentinel控制台

将该文件下载并拷贝到一个非中文路径的目录

sentinel-dashboard-1.8.1https://pan.baidu.com/s/13FRnAXR7QTN5BexD5Zb5qQ?pwd=gil1

在该目录下cmd进入控制台运行以下命令

java -jar sentinel-dashboard-1.8.1.jar

然后访问localhost:8080,输入账号和密码,都是sentinel

如果要修改sentinel的默认端口、账号、密码,可以通过下列配置:

该命令启动时,使用的端口就成了8090了

java -jar sentinel-dashboard-1.8.1.jar -Dserver.port=8090

|----------------------------------|----------|-------|
| 配置项 | 默认值 | 说明 |
| server.port | 8080 | 服务端口 |
| sentinel.dashboard.auth.username | sentinel | 默认用户名 |
| sentinel.dashboard.auth.password | sentinel | 默认密码 |

引入cloud-demo

下载该代码,用以前的那个cloud-demo也可以,不过可能需要修改一些代码:

1、所有yml文件的nacos:8848改成localhost:8848

2、所有yml文件的mysql:3306改成localhost:3306

3、有一个服务的端口是8080,这会与sentinel的端口发生冲突,需要改成其他端口

cloud-demohttps://pan.baidu.com/s/1_a4YzJr6R_EsRK6Z9tievQ?pwd=7rg1

把nacos和刚下载的sentinel启动后,再把该项目的三个服务启动,否则会报错

微服务整合sentinel

1、引入sentinel依赖

在order-service的pom文件,导入依赖

复制代码
<!--引入sentinel依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast.demo</groupId>
        <version>1.0</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>order-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--eureka客户依赖-->
<!--        <dependency>-->
<!--            <groupId>org.springframework.cloud</groupId>-->
<!--            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!--        </dependency>-->
        <!-- nacos客户端依赖包 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--feign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--引入HttpClient依赖-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>
        <!--引入feign统一的api-->
        <dependency>
            <groupId>cn.itcast.demo</groupId>
            <artifactId>feign-api</artifactId>
            <version>1.0</version>
        </dependency>
        <!--引入sentinel依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>

    </dependencies>
    <build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2、配置控制台地址

复制代码
sentinel:
  transport:
    dashboard: localhost:8080 #sentinel控制台地址
java 复制代码
server:
  port: 8088
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
    username: root
    password: 1234
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: orderservice #order的微服务名称
  cloud:
    nacos:
      server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080 #sentinel控制台地址
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 #nacos地址
            dataId: orderservice-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow #规则类型。还可以是:degrade,authority,param-flow
#      discovery:
#        cluster-name: HZ #集群名称
##        namespace: 8279562b-ce89-420a-b765-f8b2adfdbe49 #命名空间id
#        ephemeral: false #是否为临时实例
mybatis:
  type-aliases-package: cn.itcast.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
#eureka:
#  client:
#    service-url: #eureka地址信息1
#      defaultZone: http://127.0.0.1:10086/eureka
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #负载均衡规则:随机。范围:userservice服务
ribbon:
  eager-load:
    enabled: true #开启饥饿加载
    clients:  #指定饥饿加载的服务
      - userservice
#feign:
#  client:
#    config:
#      default: #这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
#        loggerLevel: FULL #日志级别
feign:
  httpclient:
    enable: true #支持httpClient的开关
    max-connections: 200 #最大连接数
    max-connections-per-route: 50 #单个路径的最大连接数

3、访问微服务的任意端点,触发sentinel监控

先访问服务的数据

再刷新查看sentinel控制台的内容

限流规则

簇点链路

簇点链路:就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源。

流控、熔断等都是针对簇点链路中的资源来设置的,因此我们可以点击对应资源后面的按钮来设置规则:

快速入门

点击资源/order/[orderld}后面的流控按钮,就可以弹出表单。表单中可以添加流控规则,如下图所示:

其含义是限制/order/{orderld}这个资源的单机QPS为5,即每秒只允许5次请求,超出的请求会被拦截并报错。

例如:现在限制为5,我在一秒内访问10次,就会有5个通过,5 个拒绝

流控模式

在添加限流规则时,点击高级选项,可以选择三种流控模式:

直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式

关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流

链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

流控模式-关联

关联模式:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流

使用场景:比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是有限支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。

当/order/update资源访问量触发阈值时,就会对/order/query资源限流,避免影响/order/update资源。

在orderController.java添加代码

复制代码
@GetMapping("/query")
public String queryOrder(){
    //查询订单
    System.out.println("查询订单");
    return "查询订单成功";
}
复制代码
@GetMapping("/update")
public String updateOrder(){
    return "更新订单成功";
}
java 复制代码
package cn.itcast.order.web;

import cn.itcast.order.pojo.Order;
import cn.itcast.order.service.OrderService;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("order")
public class OrderController {

   @Autowired
   private OrderService orderService;

    GetMapping("{orderId}")
    public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
        //根据id查询订单并返回
        return orderService.queryOrderById(orderId);
    }

    @GetMapping("/query")
    public String queryOrder(){
        //查询订单
        System.out.println("查询订单");
        return "查询订单成功";
    }


    @GetMapping("/update")
    public String updateOrder(){
        return "更新订单成功";
    }
}

重启orderservice,并访问localhost:8088/order/update和localhost:8088/order/query

再刷新sentinel控制台,会发现新增了:/order/update和/order/query两条资源名

给谁限流,就给谁加流控。要给query限流,就给它加流控

当/order/update资源访问量触发阈值时,就会对/order/query资源限流,避免影响/order/update资源。

此时会看到流控规则新增了一条

此时,每秒访问10次update,都是成功的,但此时的query被限流了,无法访问

流控模式-链路

链路模式:只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。

例如:有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。

步骤:

1、在orderService中添加一个queryGoods方法,不用实现业务

复制代码
public void queryGoods(){
    System.err.println("查询商品");
}
java 复制代码
package cn.itcast.order.service;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.pojo.User;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.用Feign远程调用
        User user = userClient.findById(order.getUserId());
        // 3.封装user到order
        order.setUser(user);
        // 4.返回
        return order;
    }

 
    public void queryGoods(){
        System.err.println("查询商品");
    }

//    @Autowired
//    private RestTemplate restTemplate;
//
//    public Order queryOrderById(Long orderId) {
//        // 1.查询订单
//        Order order = orderMapper.findById(orderId);
//        // 2.利用RestTemplate发送http请求,查询用户
//        // 2.1.url路径
//        String url = "http://userservice/user/" + order.getUserId();
//        // 2.2.发送http请求,实现远程调用
//        User user = restTemplate.getForObject(url, User.class);//第一个参数是路径,第二个参数是返回的类=类型
//        // 3.封装user到order
//        order.setUser(user);
//        // 4.返回
//        return order;
//    }
}

2、在orderController中,改造/order/query端点,调用orderService中的queryGoods方法

3、在orderController中添加一个/order/save的端点,调用orderService的queryGoods方法

复制代码
@GetMapping("/query")
public String queryOrder(){
    //查询商品
    orderService.queryGoods();
    //查询订单
    System.out.println("查询订单");
    return "查询订单成功";
}
复制代码
@GetMapping("/save")
public String saveOrder(){
    //查询商品
    orderService.queryGoods();
    //查询订单
    System.out.println("新增订单");
    return "新增订单成功";
}
java 复制代码
package cn.itcast.order.web;

import cn.itcast.order.pojo.Order;
import cn.itcast.order.service.OrderService;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("order")
public class OrderController {

   @Autowired
   private OrderService orderService;

    GetMapping("{orderId}")
    public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
        //根据id查询订单并返回
        return orderService.queryOrderById(orderId);
    }

    @GetMapping("/query")
    public String queryOrder(){
        //查询商品
        orderService.queryGoods();
        //查询订单
        System.out.println("查询订单");
        return "查询订单成功";
    }

    @GetMapping("/save")
    public String saveOrder(){
        //查询商品
        orderService.queryGoods();
        //查询订单
        System.out.println("新增订单");
        return "新增订单成功";
    }


    @GetMapping("/update")
    public String updateOrder(){
        return "更新订单成功";
    }
}

注意:因为sentinel只会监控controller的内容,但是我的controller都会访问orderService的内容,我需要监控service的内容。此时需要两步:

第一步:需要利用@SentinelResource注解

复制代码
@SentinelResource("goods")
java 复制代码
package cn.itcast.order.service;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.pojo.User;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.用Feign远程调用
        User user = userClient.findById(order.getUserId());
        // 3.封装user到order
        order.setUser(user);
        // 4.返回
        return order;
    }

    @SentinelResource("goods")
    public void queryGoods(){
        System.err.println("查询商品");
    }

//    @Autowired
//    private RestTemplate restTemplate;
//
//    public Order queryOrderById(Long orderId) {
//        // 1.查询订单
//        Order order = orderMapper.findById(orderId);
//        // 2.利用RestTemplate发送http请求,查询用户
//        // 2.1.url路径
//        String url = "http://userservice/user/" + order.getUserId();
//        // 2.2.发送http请求,实现远程调用
//        User user = restTemplate.getForObject(url, User.class);//第一个参数是路径,第二个参数是返回的类=类型
//        // 3.封装user到order
//        order.setUser(user);
//        // 4.返回
//        return order;
//    }
}

第二步: Sentinel默认会将Controller方法做context整合,导致链路模式的流控失效,需要修改application.yml添加配置

复制代码
web-context-unify: false #关闭context整合
java 复制代码
server:
  port: 8088
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
    username: root
    password: 1234
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: orderservice #order的微服务名称
  cloud:
    nacos:
      server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080 #sentinel控制台地址
      web-context-unify: false #关闭context整合
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 #nacos地址
            dataId: orderservice-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow #规则类型。还可以是:degrade,authority,param-flow
#      discovery:
#        cluster-name: HZ #集群名称
##        namespace: 8279562b-ce89-420a-b765-f8b2adfdbe49 #命名空间id
#        ephemeral: false #是否为临时实例
mybatis:
  type-aliases-package: cn.itcast.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
#eureka:
#  client:
#    service-url: #eureka地址信息1
#      defaultZone: http://127.0.0.1:10086/eureka
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #负载均衡规则:随机。范围:userservice服务
ribbon:
  eager-load:
    enabled: true #开启饥饿加载
    clients:  #指定饥饿加载的服务
      - userservice
#feign:
#  client:
#    config:
#      default: #这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
#        loggerLevel: FULL #日志级别
feign:
  httpclient:
    enable: true #支持httpClient的开关
    max-connections: 200 #最大连接数
    max-connections-per-route: 50 #单个路径的最大连接数
 

重启服务,再访问localhost:8088/order/query和localhost:8088/order/save,刷新sentinel控制台。

可以看到save和query变成了两个独立的资源名。这是因为刚才的第二步的原因

这两个goods其实是同一个,所以随便点一个,进行流控。

4、给queryGoods设置限流规则,从/order/query进入queryGoods的方法限制QPs必须小于2

监控从/order/query进来的goods,限制每秒2个。但是从/order/save进来的goods没有限制。

每秒给 /order/query和/order/save各访问4个。可以看到每秒有6个通过,2个拒绝

流控效果

流控效果是指请求达到流控阈值时应该采取的措施,包括三种:

1、快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。

2、warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。

3、排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长

流控效果-warm up

warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。而coldFactor的默认值是3

例如:给/order/{orderld}这个资源设置限流,最大QPs为10,利用warm up效果,预热时长为5秒

可以看到新增了一条规则

每秒访问10个,可以看到刚开始只能访问3个,其余的被拒绝,慢慢的可以访问到10个

流控模式-排队等待

当请求超过QPs阈值时,快速失败和warm up会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。

比如:QPS=5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常

例如:给/order/{orderld}这个资源设置限流,最大QPs为10,利用排队的流控效果,超时时长设置为5s

给上次配的warm up进行编辑,使用排队等待

可以看到warm up的规则发生了改变,变成了排队等待

每秒访问15个,可以看到,并没有出现拒绝,最多10个,因为进行了排队等待,但是响应时间挺长的

热点参数限流

之前的限流是统计访问某个资源的所有请求,判断是否超过QPs阈值。而热点参数限流是分别统计参数值相同的请求,判断是否超过QPs阈值。

例如:给/order/{orderld}这个资源添加热点参数限流,规则如下:

1、默认的热点参数规则是每1秒请求量不超过2

2、给102这个参数设置例外:每1秒请求量不超过4

3、给103这个参数设置例外:每1秒请求量不超过10

注意:热点参数限流对默认的SpringMVC资源无效。需要加注解,起名为hot

复制代码
@SentinelResource("hot")
java 复制代码
package cn.itcast.order.web;

import cn.itcast.order.pojo.Order;
import cn.itcast.order.service.OrderService;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("order")
public class OrderController {

   @Autowired
   private OrderService orderService;

   @SentinelResource("hot")
    @GetMapping("{orderId}")
    public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
        // 根据id查询订单并返回
        return orderService.queryOrderById(orderId);
    }

    @GetMapping("/query")
    public String queryOrder(){
        //查询商品
        orderService.queryGoods();
        //查询订单
        System.out.println("查询订单");
        return "查询订单成功";
    }

    @GetMapping("/save")
    public String saveOrder(){
        //查询商品
        orderService.queryGoods();
        //查询订单
        System.out.println("新增订单");
        return "新增订单成功";
    }

    @GetMapping("/update")
    public String updateOrder(){
        return "更新订单成功";
    }
}

重启服务,并访问localhost:8088/order/101,此时可以看到该资源名

按要求配置

每秒给三个请求发送5个,可以看到,最高的时候为11,因为一个限制了2,一个限制了4,一个限制了10,所以是2+4+5=11

隔离和降级

隔离和降级

虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。

不管是线程隔离还是熔断降级,都是对客户端(调用方)的保护。

Feign整合sentinel

SpringCloud中,微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel。

1、修改OrderService的application.yml文件,开启Feign的Sentinel功能

复制代码
feign:
  sentinel:
    enabled: true #开启Feign对sentinel的支持
java 复制代码
server:
  port: 8088
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
    username: root
    password: 1234
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: orderservice #order的微服务名称
  cloud:
    nacos:
      server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080 #sentinel控制台地址
      web-context-unify: false #关闭context整合
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 #nacos地址
            dataId: orderservice-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow #规则类型。还可以是:degrade,authority,param-flow
#      discovery:
#        cluster-name: HZ #集群名称
##        namespace: 8279562b-ce89-420a-b765-f8b2adfdbe49 #命名空间id
#        ephemeral: false #是否为临时实例
mybatis:
  type-aliases-package: cn.itcast.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
#eureka:
#  client:
#    service-url: #eureka地址信息1
#      defaultZone: http://127.0.0.1:10086/eureka
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #负载均衡规则:随机。范围:userservice服务
ribbon:
  eager-load:
    enabled: true #开启饥饿加载
    clients:  #指定饥饿加载的服务
      - userservice
#feign:
#  client:
#    config:
#      default: #这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
#        loggerLevel: FULL #日志级别
feign:
  httpclient:
    enable: true #支持httpClient的开关
    max-connections: 200 #最大连接数
    max-connections-per-route: 50 #单个路径的最大连接数
  sentinel:
    enabled: true #开启Feign对sentinel的支持

2、给FeignClient编写失败后的降级逻辑

方式一:FallbackClass,无法对远程调用的异常做处理

方式二:FallbackFactory,可以对远程调用的异常做处理,我们选择这种

步骤一:在feing-api的模块中的cn.itcast.feign的clients新建fallback包,新建UserClientFallbackFactory.java

java 复制代码
package cn.itcast.feign.clients.fallback;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.pojo.User;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        return new UserClient() {
            @Override
            public User findById(Long id) {
                log.error("查询用户异常",throwable);
                return new User();
            }
        };
    }
}

步骤二:在feing-api模块中的DefaultFeignConfiguration类中将UserClientFallbackFactory注册为一个Bean

复制代码
@Bean
public UserClientFallbackFactory userClientFallbackFactory(){
    return new UserClientFallbackFactory();
}
java 复制代码
package cn.itcast.feign.config;

import cn.itcast.feign.clients.fallback.UserClientFallbackFactory;
import feign.Logger;
import org.springframework.context.annotation.Bean;

public class DefaultFeignConfiguration {
    @Bean
    public Logger.Level loglevel(){
        return Logger.Level.BASIC;
    }

    @Bean
    public UserClientFallbackFactory userClientFallbackFactory(){
        return new UserClientFallbackFactory();
    }
}

步骤三:在feing-api模块中的UserClient接口中使用UserClientFallbackFactory

注意:在cloud-demo的pom文件中,找到<spring-cloud.version></spring-cloud.version>,把它写上<spring-cloud.version>Hoxton.SR8</spring-cloud.version>。不然会因为版本问题报错

java 复制代码
package cn.itcast.feign.clients;

import cn.itcast.feign.clients.fallback.UserClientFallbackFactory;
import cn.itcast.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

重启服务,并访问localhost:8088/order/101,再刷新sentinel控制台。可以看到GET的这个链路,这就是Feign的请求路径

线程隔离

线程隔离有两种方式实现:

线程池隔离

优点:支持主动超时、支持异步调用

缺点:线程的额外开销比较大

场景:低扇出
信号量隔离(Sentinel默认采用)

优点:轻量级、无额外开销

缺点:不支持主动超时、不支持异步调用

场景:高频调用、高扇出

在添加限流规则时,可以选择两种阈值类型:

QPS:就是每秒的请求数,在快速入门中已经演示过

线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现舱壁模式

例如:给UserClient的查询用户接口设置流控规则,线程数不能超过2。

对GET://http://userservice/user/{id}添加流控管理

一瞬间发送10次请求,可以看到前2个请求正常,但是后面的请求都是空而没有报错。因为UserClientFallbackFactory.java里已经写了降级逻辑,当访问被拒绝时,返回空的内容。

熔断降级

熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,

如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

断路器熔断策略有三种:慢调用、异常比例、异常数

熔断策略-慢调用

慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。

例如:给UserClient的查询用户接口设置降级规则,慢调用的RT阈值为50ms,统计时间为1秒,最小请求数量为5,失败阈值比例为0.4,熔断时长为5

提示:为了触发慢调用规则,我们需要修改UserService中的业务,增加业务耗时

在user-service的模块中,UserController.java里,修改为该代码

复制代码
@GetMapping("/{id}")                                  //请求头      变量名为Truth     可以不传
public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Truth",required = false) String truth) throws InterruptedException {
    if(id == 1){
        //休眠,触发熔断
        Thread.sleep(60);
    }
    return userService.queryById(id);
}
java 复制代码
package cn.itcast.user.web;

import cn.itcast.user.config.PatternProperties;
import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

import java.text.DateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@RestController
@RequestMapping("/user")
//@RefreshScope
public class UserController {

    @Autowired
    private UserService userService;

//    @Value("${pattern.dateformat}")
//    //注解读取该配置
//    private String dateformat;

    @Autowired
    private PatternProperties properties;//注入新的java类

    @GetMapping("prop")
    public  PatternProperties properties(){
        return properties;
    }

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat()));//通过该配置改变时间格式
    }

    /**
     * 路径: /user/110
     *
     * @param id 用户id
     * @return 用户
     */
    @GetMapping("/{id}")                                  //请求头      变量名为Truth     可以不传
    public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Truth",required = false) String truth) throws InterruptedException {
        if(id == 1){
            //休眠,触发熔断
            Thread.sleep(60);
        }
        return userService.queryById(id);
    }
}

重启服务,访问localhost:8088/order/101,把以前的GET://http://userservice/user/{id}流控规则删掉。

给GET://http://userservice/user/{id}配置降级

可以看到新增的规则

一秒内请求5次localhost:8088/order/101,之后再请求localhost:8088/order/102会发现数据为空

熔断策略-异常比例、异常数

异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。

例如:给UserClient的查询用户接口设置降级规则,统计时间为1秒,最小请求数量为5,失败阈值比例为0.4,熔断时长为5S

提示:为了触发异常统计,我们需要修改UserService中的业务,抛出异常:

复制代码
@GetMapping("/{id}")                                  //请求头      变量名为Truth     可以不传
public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Truth",required = false) String truth) throws InterruptedException {
    if(id == 1){
        //休眠,触发熔断
        Thread.sleep(60);
    }else if(id == 2){
        throw new RuntimeException("故意出错,触发熔断");
    }
    return userService.queryById(id);
}
java 复制代码
package cn.itcast.user.web;

import cn.itcast.user.config.PatternProperties;
import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

import java.text.DateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@RestController
@RequestMapping("/user")
//@RefreshScope
public class UserController {

    @Autowired
    private UserService userService;

//    @Value("${pattern.dateformat}")
//    //注解读取该配置
//    private String dateformat;

    @Autowired
    private PatternProperties properties;//注入新的java类

    @GetMapping("prop")
    public  PatternProperties properties(){
        return properties;
    }

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat()));//通过该配置改变时间格式
    }

    /**
     * 路径: /user/110
     *
     * @param id 用户id
     * @return 用户
     */
    @GetMapping("/{id}")                                  //请求头      变量名为Truth     可以不传
    public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Truth",required = false) String truth) throws InterruptedException {
        if(id == 1){
            //休眠,触发熔断
            Thread.sleep(60);
        }else if(id == 2){
            throw new RuntimeException("故意出错,触发熔断");
        }
        return userService.queryById(id);
    }
}

重启服务,访问localhost:8088/order/101,把以前的GET://http://userservice/user/{id}流控规则删掉。

给GET://http://userservice/user/{id}配置降级

访问5次localhost:8088/order/102,触发熔断规则,再访问localhost:8088/order/103,会发现数据也为空

授权规则及规则持久化

授权规则

授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。

1、白名单:来源(origin)在白名单内的调用者允许访问

2、黑名单:来源(origin)在黑名单内的调用者不允许访问

例如,我们尝试从request中获取一个名为origin的请求头,作为origin的值

在order-service模块中的cn.itcast.order中新增一个sentinel包,新增HeaderOriginParser.java

java 复制代码
package cn.itcast.order.sentinel;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import io.netty.util.internal.StringUtil;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class HeaderOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        //1、获取请求头
        String origin = request.getHeader("origin");
        //2、非空判断
        if(StringUtils.isEmpty(origin)){
            origin = "blank";
        }
        return origin;
    }
}

需要在gateway服务中,利用网关的过滤器添加名为gateway的origin头

复制代码
- AddRequestHeader=origin,gateway #添加情求头
java 复制代码
server:
  port: 10010 #网关端口
spring:
  application:
    name: gateway #服务名称
  cloud :
    nacos:
      server-addr: localhost:8848 #nacos地址
    gateway:
      routes: #网关路由配置
        - id: user-service #路由id,自定义,只要唯一即可
        # uri: http://127.0.0.1:8081 #路由的目标地址 http就是固定地址
          uri: lb://userservice #路由的目标地址lb就是负载均衡,后面跟服务名称
          predicates: #路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** #这个是按照路径匹配,只要以/user/开头就符合要求
#          filters:
#            - AddRequestHeader=Truth,Itcast is freaking awesome! #添加请求头
        - id: order-service #路由id,自定义,只要唯一即可
          uri: lb://orderservice #路由的目标地址lb就是负载均衡,后面跟服务名称
          predicates: #路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/order/** #这个是按照路径匹配,只要以/user/开头就符合要求
#            - After=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] #要求访问时间在这个之后
            - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] #要求访问时间在这个之后
      default-filters: #默认过滤器,会对所有的路由请求都生效
        - AddRequestHeader=Truth,Itcast is freakina awesome! #添加情求头
        - AddRequestHeader=origin,gateway #添加情求头
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求
              - "http://localhost:8090"
              - "http://www.leyou.com"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

重启服务,访问localhost:8088/order/101给/order/{orderld}配置授权规则

访问 localhost:8088/order/103报错,访问localhost:8088/order/103?authorization=admin成功

自定义异常结果

默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口

在order-service模块中的cn.itcast.order中的sentinel包,新增SentinelExceptionHandler.java

java 复制代码
package cn.itcast.order.sentinel;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        String msg = "未知异常";
        int status = 429;

        if (e instanceof FlowException) {
            msg = "请求被限流了";
        } else if (e instanceof ParamFlowException) {
            msg = "请求被热点参数限流";
        } else if (e instanceof DegradeException) {
            msg = "请求被降级了";
        } else if (e instanceof AuthorityException) {
            msg = "没有权限访问";
            status = 401;
        }

        response.setContentType("application/json;charset=utf-8");
        response.setStatus(status);
        response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
    }
}

重启服务, 访问 localhost:8088/order/103,给/order/{orderId}添加流控规则

访问2次localhost:8088/order/103,出现限流

把刚配的限流规则删掉,新增授权规则

这次为没有权限

规则管理模式

sentinel的控制台规则管理有三种模式:

1、原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失

2、pull模式

3、push模式

pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。

push模式:控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。

实现push模式

push模式实现最为复杂,依赖于nacos,并且需要修改Sentinel控制台源码。

1、引入依赖

在order-service中引入sentinel监听nacos的依赖

复制代码
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast.demo</groupId>
        <version>1.0</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>order-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--eureka客户依赖-->
<!--        <dependency>-->
<!--            <groupId>org.springframework.cloud</groupId>-->
<!--            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!--        </dependency>-->
        <!-- nacos客户端依赖包 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--feign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--引入HttpClient依赖-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>
        <!--引入feign统一的api-->
        <dependency>
            <groupId>cn.itcast.demo</groupId>
            <artifactId>feign-api</artifactId>
            <version>1.0</version>
        </dependency>
        <!--引入sentinel依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
    </dependencies>
    <build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

配置nacos地址

在order-service中的application.yml文件配置nacos地址及监听的配置信息

复制代码
spring:
  cloud:
    sentinel:
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 #nacos地址
            dataId: orderservice-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow #规则类型。还可以是:degrade,authority,param-flow
java 复制代码
server:
  port: 8088
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
    username: root
    password: 1234
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: orderservice #order的微服务名称
  cloud:
    nacos:
      server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080 #sentinel控制台地址
      web-context-unify: false #关闭context整合
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 #nacos地址
            dataId: orderservice-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow #规则类型。还可以是:degrade,authority,param-flow
#      discovery:
#        cluster-name: HZ #集群名称
##        namespace: 8279562b-ce89-420a-b765-f8b2adfdbe49 #命名空间id
#        ephemeral: false #是否为临时实例
mybatis:
  type-aliases-package: cn.itcast.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
#eureka:
#  client:
#    service-url: #eureka地址信息1
#      defaultZone: http://127.0.0.1:10086/eureka
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #负载均衡规则:随机。范围:userservice服务
ribbon:
  eager-load:
    enabled: true #开启饥饿加载
    clients:  #指定饥饿加载的服务
      - userservice
#feign:
#  client:
#    config:
#      default: #这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
#        loggerLevel: FULL #日志级别
feign:
  httpclient:
    enable: true #支持httpClient的开关
    max-connections: 200 #最大连接数
    max-connections-per-route: 50 #单个路径的最大连接数
  sentinel:
    enabled: true #开启Feign对sentinel的支持

重启服务

下载该文件,并把它放到跟sentinel-dashboard-1.8.1同一目录下

sentinel-dashboard.jarhttps://pan.baidu.com/s/1MLvwJiHHrntl9vals3yFNQ?pwd=egh4

在该目录下cmd进入控制台运行以下命令

java -jar -Dnacos.addr=localhost:8848 sentinel-dashboard.jar

访问loaclhost:8848,进入nacos,访问localhost:8088/order/103, 刷新sentinel控制台,可以看到新的列表

在这里新增的流控规则会作用到nacos,点新增

此时刷新nacos,可以看到新的配置

访问localhost:8088/order/103,发现成功限流

再重启服务,看sentinel控制台的规则是否被重置

可以看到规则还在,规则持久化完成

代码文件,点击下载https://pan.baidu.com/s/1sdroaa2GxZ_L3nq9BKqLzQ?pwd=i0et

上一篇:数据聚合、自动补全、数据同步、es集群

相关推荐
金銀銅鐵33 分钟前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
福建佰胜张工1 小时前
SMC 薄型气爪 MHF2-16D1 深度解析:平行开闭、低剖面结构在自动化装配中的选型与落地
java·服务器·安全·自动化
catoop1 小时前
API Key 设计与安全生成方案:结构拆解、数据库设计与 Java 实现
java·数据库·安全
system_error_x2 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
驭渊的小故事2 小时前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring
Elastic 中国社区官方博客2 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1883 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn3 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su4 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
我命由我123455 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法