跳至主要內容

Sentinel

sixkey大约 18 分钟后端SpringCloud微服务Sentinel

Sentinel

CloudAlibaba之Sentinel

简介

官网:

介绍 · alibaba/Sentinel Wiki (github.com)open in new window

home | Sentinel (sentinelguard.io)open in new window

是什么?

轻量级的流量控制,熔断降级的java库

去哪下

Releases · alibaba/Sentinel (github.com)open in new window

能干嘛?

从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性

怎么玩?

  • 服务雪崩

    • 多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

      所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。

      screenshot-1713061519690
  • 服务降级

    • 服务降级,说白了就是一种服务托底方案,如果服务无法完成正常的调用流程,就使用默认的托底方案来返回数据。

      例如,在商品详情页一般都会展示商品的介绍信息,一旦商品详情页系统出现故障无法调用时,会直接获取缓存中的商品介绍信息返回给前端页面。

  • 服务熔断

    • 在分布式与微服务系统中,如果下游服务因为访问压力过大导致响应很慢或者一直调用失败时,上游服务为了保证系统的整体可用性,会暂时断开与下游服务的调用连接。这种方式就是熔断。类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。

      服务熔断一般情况下会有三种状态:闭合、开启和半熔断;

      闭合状态(保险丝闭合通电OK):服务一切正常,没有故障时,上游服务调用下游服务时,不会有任何限制。

      开启状态(保险丝断开通电Error):上游服务不再调用下游服务的接口,会直接返回上游服务中预定的方法。

      半熔断状态:处于开启状态时,上游服务会根据一定的规则,尝试恢复对下游服务的调用。此时,上游服务会以有限的流量来调用下游服务,同时,会监控调用的成功率。如果成功率达到预期,则进入关闭状态。如果未达到预期,会重新进入开启状态。

  • 服务限流

    • 服务限流就是限制进入系统的流量,以防止进入系统的流量过大而压垮系统。其主要的作用就是保护服务节点或者集群后面的数据节点,防止瞬时流量过大使服务和数据崩溃(如前端缓存大量实效),造成不可用;还可用于平滑请求,类似秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。

      限流算法有两种,一种就是简单的请求总量计数,一种就是时间窗口限流(一般为1s),如令牌桶算法和漏牌桶算法就是时间窗口的限流算法。

  • 服务隔离

    • 有点类似于系统的垂直拆分,就按照一定的规则将系统划分成多个服务模块,并且每个服务模块之间是互相独立的,不会存在强依赖的关系。如果某个拆分后的服务发生故障后,能够将故障产生的影响限制在某个具体的服务内,不会向其他服务扩散,自然也就不会对整体服务产生致命的影响。

      互联网行业常用的服务隔离方式有:线程池隔离和信号量隔离。

  • 服务超时

    • 整个系统采用分布式和微服务架构后,系统被拆分成一个个小服务,就会存在服务与服务之间互相调用的现象,从而形成一个个调用链。

      形成调用链关系的两个服务中,主动调用其他服务接口的服务处于调用链的上游,提供接口供其他服务调用的服务处于调用链的下游。服务超时就是在上游服务调用下游服务时,设置一个最大响应时间,如果超过这个最大响应时间下游服务还未返回结果,则断开上游服务与下游服务之间的请求连接,释放资源。

安装Sentinel

Sentinel由两部分组成

screenshot-1713061797249
screenshot-1713061797249

安装步骤

下载

Releases · alibaba/Sentinel (github.com)open in new window

下载到本地sentinel-dashboard-1.8.7.jar

运行命令

  • 前提
    • java环境ok
    • 8080端口不能被占用
  • 命令
    • java -jar sentinel-dashboard-1.8.7.jar

访问sentinel管理界面

http://localhost:8080

账号密码均为sentinel

微服务8401整合案例

新建微服务8401

新建微服务cloudAlibaba-sentinel-service8401:意味着8401微服务将被哨兵纳入监控

改pom

<properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--SpringCloud alibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--nacos-discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包 -->
        <dependency>
            <groupId>org.cloud</groupId>
            <artifactId>cloud-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--SpringBoot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
            <scope>provided</scope>
        </dependency>
        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

写yaml

server:
  port: 8401

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

主启动类

@SpringBootApplication
@EnableDiscoveryClient
public class SentinelApplication
{
    public static void main( String[] args )
    {
        SpringApplication.run(SentinelApplication.class,args);
    }
}

业务类

@RestController
public class FlowLimitController
{

    @GetMapping("/testA")
    public String testA()
    {
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB()
    {
        return "------testB";
    }
}

启动8401服务查看Sentinel控制台

  • 空空如也,啥也没有

  • Sentinel采用的懒加载说明

    • 注意:想使用Sentinel对某个接口进行限流和降级等操作,一定要先访问下接口,使Sentinel检测到相应的接口
    • 执行一次访问即可
      • http://localhost:8401/testA
      • http://localhost:8401/testB
    • 效果
      • 24f33ae69639b534d40c60c71fb2b893.png
        24f33ae69639b534d40c60c71fb2b893.png

流控规则

基本介绍

Sentinel能够对流量进行控制,主要是监控应用的QPS流量或者并发线程数等指标,如果达到指定的阈值时,就会被流量进行控制,以避免服务被瞬时的高并发流量击垮,保证服务的高可靠性。参数见最下方:

5292d2a5f0a45558ef01bb784f543cc7.png
1资源名资源的唯一名称,默认就是请求的接口路径,可以自行修改,但是要保证唯一。
2针对来源具体针对某个微服务进行限流,默认值为default,表示不区分来源,全部限流。
3阈值类型QPS表示通过QPS进行限流,并发线程数表示通过并发线程数限流。
4单机阈值与阈值类型组合使用。如果阈值类型选择的是QPS,表示当调用接口的QPS达到阈值时,进行限流操作。如果阈值类型选择的是并发线程数,则表示当调用接口的并发线程数达到阈值时,进行限流操作。
5是否集群选中则表示集群环境,不选中则表示非集群环境。

流控模式

  • 直接

    • 默认的流控模式,当接口达到限流条件时,直接开启限流功能。
    • 配置及说明
      • 表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
      • 7c5da6eb7dba38a890cf1cc186fcde48.png
    • 测试
      • 快速点击访问http://localhost:8401/testA
      • 结果:Blocked by Sentinel (flow limiting)
      • 思考?直接调用默认报错信息,技术方面OK,是否应该有我们自己的后续处理?类似有个fallback的兜底方法?
  • 关联

    • 是什么?
      • 当关联的资源达到阈值时,就限流自己 当与A关联的资源B达到阀值后,就限流A自己 B惹事,A挂了
    • 配置A
      • 设置效果:当关联资源/testB的qps阀值超过1时,就限流/testA的Rest访问地址,当关联资源到阈值后限制配置好的资源名,B惹事,A挂了
      • b09e0ba5f7f209bcbbac995eb63c0799.png
      • 测试:大批量线程高并发访问B,导致A失效,访问http://localhost:8401/testA,结果:Blocked by Sentinel (flow limiting)
  • 链路

    • 是什么?

      • 来自不同链路的请求对同一个目标访问时,实施针对性的不同限流措施, 比如C请求来访问就限流,D请求来访问就是OK
    • 修改微服务cloudAlibaba-sentinel-service8401

      • 改yaml

        server:
          port: 8401
        
        spring:
          application:
            name: cloudalibaba-sentinel-service #8401微服务提供者后续将会被纳入阿里巴巴sentinel监管
          cloud:
            nacos:
              discovery:
                server-addr: localhost:8848         #Nacos服务注册中心地址
            sentinel:
              transport:
                dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址
                        port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
                    web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路
        
      • 业务类

        新建FlowLimitService

        @Service
        public class FlowLimitService
        {
            @SentinelResource(value = "common")
            public void common()
            {
                System.out.println("------FlowLimitService come in");
            }
        }
        
        

        修改FlowLimitController

        @RestController
        public class FlowLimitController
        {
        
            @GetMapping("/testA")
            public String testA()
            {
                return "------testA";
            }
        
            @GetMapping("/testB")
            public String testB()
            {
                return "------testB";
            }
        
            /**流控-链路演示demo
             * C和D两个请求都访问flowLimitService.common()方法,阈值到达后对C限流,对D不管
             */
            @Resource private FlowLimitService flowLimitService;
        
            @GetMapping("/testC")
            public String testC()
            {
                flowLimitService.common();
                return "------testC";
            }
            @GetMapping("/testD")
            public String testD()
            {
                flowLimitService.common();
                return "------testD";
            }
        }
        
    • Sentinel配置

      • 说明:C和D两个请求都访问flowLimitService.common()方法,对C限流,对D不管
      • 789248abb289074dca4626f6e224d960.png
    • 测试

      • 访问http://localhost:8401/testC
      • C链路
        • 85283c11ae29a78d997fc2af1db9f559.png
      • D链路ok

流控效果

339ff2f409f9ec8852081f9990d6ad21.png
339ff2f409f9ec8852081f9990d6ad21.png

熔断规则

介绍

Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。

熔断规则实战

慢调用比例
异常比例
异常数

@SentinelResource注解

是什么?

@SentinelResource是一个流量防卫防护组件注解,用于指定防护资源,对配置的资源进行流量控制,熔断降级等功能。

实战

默认限流返回

安装rest地址限流 + 默认限流返回

  • 通过访问的rest地址来限流,会返回sentinel自带默认的限流处理信息

  • 新建业务类RateLimitController

    @RestController
    @Slf4j
    public class RateLimitController
    {
        @GetMapping("/rateLimit/byUrl")
        public String byUrl()
        {
            return "按rest地址限流测试OK";
        }
    }
    
  • 访问一次:http://localhost:8401/rateLimit/byUrl 没啥事

  • Sentinel控制台配置

    • be046003db348f23a2e69b1c468a31b1.png
  • 测试:疯狂点击http://localhost:8401/rateLimit/byUrl

    • 结果:
    • ec16b6583ccbb39ab6c271644a123e18.png
自定义限流返回

不想要默认自带的限流提示Blocked by Sentinel (flow limiting),想返回自定义的限流提示

  • 修改业务类RateLimitController

    @RestController
    @Slf4j
    public class RateLimitController
    {
        @GetMapping("/rateLimit/byUrl")
        public String byUrl()
        {
            return "按rest地址限流测试OK";
        }
    
    @GetMapping("/rateLimit/byResource")
    @SentinelResource(value = "byResourceSentinelResource",blockHandler = "handleException")
    public String byResource()
    {
        return "按资源名称SentinelResource限流测试OK";
    }
    public String handleException(BlockException exception)
    {
        return "服务不可用@SentinelResource启动"+"\t"+"o(╥﹏╥)o";
    }
    }
    
  • 测试地址:http://localhost:8401/rateLimit/byResource

  • Sentinel控制台配置

    3d3c481983833be6a9c30c2f884d992a.png6b6f1e5cc8816335c0c13159c8ef1094.png
  • 疯狂点击:http://localhost:8401/rateLimit/byResource

    • 返回自定义限流提示

      f1dec737ef7e9a49b4d14a981ac8f7e0.png
      f1dec737ef7e9a49b4d14a981ac8f7e0.png
自定义限流 + 服务降级处理

按照SentinelResource配置,点击超过限流配置返回自定义限流提示 + 程序异常返回fallbakc服务降级处理

  • 修改业务类RateLimitController

    @RestController
    @Slf4j
    public class RateLimitController
    {
        @GetMapping("/rateLimit/byUrl")
        public String byUrl()
        {
            return "按rest地址限流测试OK";
        }
    
        @GetMapping("/rateLimit/byResource")
        @SentinelResource(value = "byResourceSentinelResource",blockHandler = "handleException")
        public String byResource()
        {
            return "按资源名称SentinelResource限流测试OK";
        }
        public String handleException(BlockException exception)
        {
            return "服务不可用@SentinelResource启动"+"\t"+"o(╥﹏╥)o";
        }
    
        @GetMapping("/rateLimit/doAction/{p1}")
        @SentinelResource(value = "doActionSentinelResource",
                blockHandler = "doActionBlockHandler", fallback = "doActionFallback")
        public String doAction(@PathVariable("p1") Integer p1) {
            if (p1 == 0){
                throw new RuntimeException("p1等于零直接异常");
            }
            return "doAction";
        }
    
        public String doActionBlockHandler(@PathVariable("p1") Integer p1,BlockException e){
            log.error("sentinel配置自定义限流了:{}", e);
            return "sentinel配置自定义限流了";
        }
    
        public String doActionFallback(@PathVariable("p1") Integer p1,Throwable e){
            log.error("程序逻辑异常了:{}", e);
            return "程序逻辑异常了"+"\t"+e.getMessage();
        }
    
    }
    
  • 访问地址:http://localhost:8401/rateLimit/doAction/2

  • Sentinel控制台配置

    e7d0dfd651c039377a4361c571e705ca.png
  • 图形配置和代码关系

    416fe8f99ae3f95ee0467afa1d709f33.png
  • 测试:

    • 疯狂访问:http://localhost:8401/rateLimit/doAction/2,返回了自定义限流提示

      1f6a8b13f25126deef6df63658f3238e.png
      1f6a8b13f25126deef6df63658f3238e.png
    • p1参数为0,访问:http://localhost:8401/rateLimit/doAction/0,异常发生,返回了自定义的fallback服务降级处理

      603fb91522eb078909ca8a287480cc8e.png
      603fb91522eb078909ca8a287480cc8e.png
小结

blockHandler:主要针对sentinel配置后出现的违规情况处理

fallback:程序出现了异常,JVM抛出的异常服务降级

两者可以同时共存

热点规则

授权规则

规则持久化

Sentinel集成openfeign

作用?

实现fallback服务降级

需求说明

cloudalibaba-consumer-nacos-order83 通过OpenFeign调用 cloudalibaba-provider-payment9001

1 、83 通过OpenFeign调用 9001微服务,正常访问OK

2 、83 通过OpenFeign调用 9001微服务,异常访问error

访问者要有fallback服务降级的情况,不要持续访问9001加大微服务负担,但是通过feign接口调用的又方法各自不同,

如果每个不同方法都加一个fallback配对方法,会导致代码膨胀不好管理,工程埋雷....../(ㄒoㄒ)/~~

3 、public @interface FeignClient

通过fallback属性进行统一配置,feign接口里面定义的全部方法都走统一的服务降级,一个搞定即可

4、 9001微服务自身还带着sentinel内部配置的流控规则,如果满足也会被触发,也即本例有2个Case

4.1 、OpenFeign接口的统一fallback服务降级处理

4.2 、Sentinel访问触发了自定义的限流配置,在注解@SentinelResource里面配置的blockHandler方法。

编码步骤

修改服务提供方

1、修改微服务cloudAlibaba-provider-payment9001

  • 改pom:新增sentinel依赖即可,目的就是让服务提供方9001被哨兵sentinel监控
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--alibaba-sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
  • 写yaml:添加sentinel配置即可
spring:
  application:
    name: cloud-payment-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        enabled: true
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址
        port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口

添加一个被sentinel哨兵监控的业务类用来测试

/**
 * ClassName: PaySentinelController
 * Package: org.cloud.controller
 * Description:
 *
 * @Author: @weixueshi
 * @Create: 2024/4/14 - 15:20
 * @Version: v1.0
 */
@RestController
public class PaySentinelController {

    @GetMapping("/pay/sentinel/get/{orderNo}")
    @SentinelResource(value = "getPayByOrderNo",blockHandler = "handlerBlockHandler")
    public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo)
    {
        //模拟从数据库查询出数据并赋值给DTO
        PayDto payDto = new PayDto();
        payDto.setOrderNo(orderNo);
        payDto.setPayNo("pay:"+ IdUtil.fastUUID());
        payDto.setUserId(1);

        return ResultData.success("查询返回值:"+payDto);
    }
    public ResultData handlerBlockHandler(@PathVariable("orderNo") String orderNo, BlockException exception)
    {
        return ResultData.fail(ResponseCodeEnum.BAD_REQUEST.getCode(),"getPayByOrderNo服务不可用," +
                "触发sentinel流控配置规则"+"\t"+"o(╥﹏╥)o");
    }
    /*
    fallback服务降级方法纳入到Feign接口统一处理,全局一个
    public ResultData myFallBack(@PathVariable("orderNo") String orderNo,Throwable throwable)
    {
        return ResultData.fail(ReturnCodeEnum.RC500.getCode(),"异常情况:"+throwable.getMessage());
    }
    */
}

修改openfeign服务

改pom:添加sentinel依赖

<!--alibaba-sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

修改远程调用接口,并且添加fallback处理类

package org.cloud.apis;

import io.swagger.v3.oas.annotations.Operation;
import org.cloud.apis.fallback.PayFeignSentinelApiFallBack;
import org.cloud.model.dto.PayDto;
import org.cloud.response.ResultData;
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;

/**
 * ClassName: AliPaymentFeign
 * Package: org.cloud.apis
 * Description:
 *
 * @Author: @weixueshi
 * @Create: 2024/4/13 - 14:57
 * @Version: v1.0
 */
@FeignClient(name = "cloud-payment-service",fallback = PayFeignSentinelApiFallBack.class)
public interface AliPaymentFeign {
    /**
     * sentinel哨兵监控测试
     * @param orderNo
     * @return
     */
    @GetMapping("/pay/sentinel/get/{orderNo}")
    ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo);
}

新建PayFeignSentinelApiFallBack.class处理fallback逻辑,这就是全局唯一的服务降级处理

/**
 * ClassName: PayFeignSentinelApiFallBack
 * Package: org.cloud.apis.fallback
 * Description:
 *
 * @Author: @weixueshi
 * @Create: 2024/4/14 - 16:06
 * @Version: v1.0
 */
@Component
public class PayFeignSentinelApiFallBack implements AliPaymentFeign {
    @Override
    public ResultData getPayByOrderNo(String orderNo)
    {
        return ResultData.fail(ResponseCodeEnum.BAD_REQUEST.getCode(),"对方服务宕机或不可用,FallBack服务降级o(╥﹏╥)o");
    }
}

新建服务消费方

改pom

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.cloud</groupId>
        <artifactId>cloud2024</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloudAlibaba-consumer-payment83</artifactId>
    <dependencies>
        <!--自定义openfeign服务-->
        <dependency>
            <groupId>org.cloud</groupId>
            <artifactId>cloudAlibaba-openfeign-consumer</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--alibaba-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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--自定义-->
        <dependency>
            <groupId>org.cloud</groupId>
            <artifactId>cloud-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--web + actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--fastjson2-->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
    </dependencies>

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

写yaml

server:
  port: 83

spring:
  application:
    name: sentinel-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  main:
    allow-bean-definition-overriding: true

# 激活Sentinel对Feign的支持
feign:
  sentinel:
    enabled: true

添加启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerPayment83App
{
    public static void main( String[] args )
    {
        SpringApplication.run(ConsumerPayment83App.class,args);
    }
}

添加业务类

@RestController
@RequiredArgsConstructor
public class ConsumerPaySentinelController {

    private final AliPaymentFeign aliPaymentFeign;

    @GetMapping("/consumer/pay/sentinel/get/{orderNo}")
    public ResultData getPayment(@PathVariable("orderNo") String orderNo){
        return aliPaymentFeign.getPayByOrderNo(orderNo);
    }
}

启动服务消费方时报错:

67d01bb958e455716eeba7200e6c89d7.png

解决方案

5dbe439d8eada9b0578bbeae21f6c300.png
测试

以上三个服务全部启动成功后,测试访问地址为(这个时候sentinel还未做任何的配置)

http://localhost:83/consumer/pay/sentinel/get/147

测试:即使疯狂点击进行请求handlerBlockHandler和fallback的逻辑也不会生效

配置sentinel

56d5754f1da5e1d6aceb181bf9afe4f8.pngf1d43338c867393c060afb4ea58628e5.png
  • 测试handlerBlockHandler是否生效:疯狂点击http://localhost:83/consumer/pay/sentinel/get/147

    • 结果触发了handlerBlockHandler方法,并输出自定义限流提示

      ebb82e4451e3e3e4f2a624cba3d5ced7.png
  • 测试fallback是否生效:将访问提供者9001宕机

    • 结果触发了PayFeignSentinelApiFallBack类中的方法,并输出自定义服务降级提示

      06fe25e70289b74313228e18bc9d839e.png

Sentinel与openfeign集成完毕

Sentinel集成gateway

需求说明

由Sentinel集成gateway通过限流方式来保护服务提供者cloud-Alibaba-provider-payment9001

集成步骤

建Model

cloudAlibaba-sentinel-gateway9528

改pom
<dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.8.6</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
            <version>1.8.6</version>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
写yaml
server:
  port: 9528

spring:
  application:
    name: cloudAlibaba-sentinel-gateway9528 #以微服务注册进consul
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: pay_routh1
          uri: lb://sentinel-order-consumer
          predicates:
            - Path=/consumer/**
配置类(重要)

参考官网配置说明案例改写

/**
 * ClassName: GatewayConfiguration
 * Package: org.cloud.config
 * Description:
 *
 * @Author: @weixueshi
 * @Create: 2024/4/14 - 22:17
 * @Version: v1.0
 */

/**
 * 使用时只需注入对应的 SentinelGatewayFilter
 * 实例以及 SentinelGatewayBlockExceptionHandler 实例即可
 */
@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer)
    {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * javax.annotation.PostConstruct
     */
    @PostConstruct
    public void doInit() {
        initBlockHandler();
    }


    //处理/自定义返回的例外信息
    private void initBlockHandler() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        /**
         * 参数1:网关唯一id
         * 参数2、3:1秒钟2个请求,超过触发限流
         */
        rules.add(new GatewayFlowRule("pay_routh1").setCount(2).setIntervalSec(1));

        GatewayRuleManager.loadRules(rules);
        BlockRequestHandler handler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
                Map<String,String> map = new HashMap<>();

                map.put("errorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
                map.put("errorMessage", "系统繁忙,稍后重试!");

                return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(handler);
    }

}
测试
  • 原生url(不加网关)

    • http://localhost:9001/pay/sentinel/get/333
  • 加网关

    • http://localhost:9528/consumer/pay/sentinel/get/888

    • Sentinel + gateway:加快点击频率,出现限流错误

      825f519931b94f2414d0d8c748f2684d.png
      825f519931b94f2414d0d8c748f2684d.png