我是靠谱客的博主 爱笑冰淇淋,这篇文章主要介绍Spring Cloud Alibaba】Sentinel 流量控制1 搭建SpringCloud Alibaba Sentinel控制台2 项目集成3 降级4 Sentinel 存储5 Gateway 结合 Sentinel,现在分享给大家,希望可以做个参考。

【Spring Cloud Alibaba】Sentinel 流量控制

  • 1 搭建SpringCloud Alibaba Sentinel控制台
  • 2 项目集成
  • 3 降级
    • 3.1 接口降级
      • 3.1.2 硬编码限流
      • 3.1.2 控制面板限流
    • 3.2 RestTemplate 降级
    • 3.3 Feign 降级
  • 4 Sentinel 存储
    • 4.1 SpringCloud Alibaba Sentinel 结合 Nacos
  • 5 Gateway 结合 Sentinel
    • 5.1 添加依赖
    • 5.2 硬编码配置
    • 5.3 JSON文件配置方式
    • 5.3 Nacos存储配置方式

Sentinel是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性

SpringCloud Alibaba Sentinel流量控制方向

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系
  • 运行指标,例如QPS、线程池、系统负载等
  • 控制的效果,例如直接限流、冷启动、排队等

1 搭建SpringCloud Alibaba Sentinel控制台

Sentinel提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能

获取并启动Sentinel Dashboard(控制台)

1.下载控制台Jar包: https://github.com/alibaba/Sentinel/releases
2. java -Dserver.port=7777 -Dcsp.sentinel.dashboard.server=localhost:7777 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
3.从 Sentinel 1.6.0起,Sentinel Dashboard引入了基本的登录功能,默认的用
户名密码都是sentinel

2 项目集成

创建子模块,配置maven

<artifactId>e-commerce-sentinel-client</artifactId>
<packaging>jar</packaging>
<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- 模块名及描述信息 -->
<name>e-commerce-sentinel-client</name>
<description>Sentinel Client</description>

<dependencies>
    <!-- 创建工程需要的两个依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!-- Sentinel 适配了 Feign, 可以实现服务间调用的保护 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!-- Sentinel 使用 Nacos 存储规则 -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <!-- web 工程 -->
    <dependency>
        <groupId>cn.flowboot.e.commerce</groupId>
        <artifactId>e-commerce-common</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<!--
    SpringBoot的Maven插件, 能够以Maven的方式为应用提供SpringBoot的支持,可以将
    SpringBoot应用打包为可执行的jar或war文件, 然后以通常的方式运行SpringBoot应用
 -->
<build>
    <finalName>${artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

3 降级

SpringCloud Alibaba Sentinel对降级功能的支持

3.1 接口降级

3.1.2 硬编码限流

FlowRuleCodeController

package cn.flowboot.e.commerce.controller;

import cn.flowboot.e.commerce.block.handler.MyBlockHandler;
import cn.flowboot.e.commerce.vo.CommonResponse;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * <h1>流控规则硬编码</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/17
 */
@Slf4j
@RestController
@RequestMapping("/code")
public class FlowRuleCodeController {

    /**
     * 初始化流控规则
     */
    @PostConstruct
    public void init(){
        //流控规则集合
        List<FlowRule> flowRules = new ArrayList<>();
        //创建流控规则
        FlowRule flowRule = new FlowRule();
        //设置流控规则QPS,限流阈值类型CQPS,并发线程数
        flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //流量控制手段
        flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        //设置受保护的资源
        flowRule.setResource("flowRuleCode");
        //设置受保护的资源的阈值
        flowRule.setCount(1);
        flowRules.add(flowRule);

        //加载配置好的规则
        FlowRuleManager.loadRules(flowRules);
    }

    /**
     * <h1>采用硬编码的限流规则的Controller方法</h1>
     * @return
     */
    @GetMapping("/flow-rule")
    //@SentinelResource(value = "flowRuleCode")
    //@SentinelResource(value = "flowRuleCode",blockHandler = "handleException")
    @SentinelResource(value = "flowRuleCode",blockHandler = "myHandleException",blockHandlerClass = MyBlockHandler.class)
    public CommonResponse<String> flowRuleCode(){
        log.info("request flowRuleCode");
        return CommonResponse.success();
    }

    /**
     * <h2>当限流异常抛出时,指定调用的方法</h2>
     * 是一个兜底策略
     */
    public CommonResponse handleException(BlockException e){
        log.error("has block exception : [{}]", JSONObject.toJSONString(e.getRule()));
        return CommonResponse.fail("flow rule exception",e.getClass().getCanonicalName());
    }

}

上文flowRuleCode方法注解使用到通用限流处理MyBlockHandler ,可切换注释使用不同使用策略

package cn.flowboot.e.commerce.block.handler;

import cn.flowboot.e.commerce.vo.CommonResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

/**
 * <h1>自定义通用处理逻辑</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/17
 */
@Slf4j
public class MyBlockHandler {

    /**
     * <h2>通用限流处理方法</h2>
     * 这个方法必须是static的
     **/
    public static CommonResponse<String> myHandleException(BlockException e){
        log.error("has block exception : [{}], [{}]", JSONObject.toJSONString(e.getRule()),e.getRuleLimitApp());
        return CommonResponse.fail("trigger flow rule exception",e.getClass().getCanonicalName());
    }

}

3.1.2 控制面板限流

RateLimitController

package cn.flowboot.e.commerce.controller;

import cn.flowboot.e.commerce.block.handler.MyBlockHandler;
import cn.flowboot.e.commerce.vo.CommonResponse;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * <h1>基于Sentinel 控制台配置流控规则</h1>
 * Sentinel 是懒加载的,先去访问一下,就可以在 Sentinel Dashboard看到了
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/17
 */
@Slf4j
@RestController
@RequestMapping("/rate")
public class RateLimitController {



    /**
     * <h2>在 dashboard 中“流控规则”中按照资源名称新增流控规则</h2>
     * @return
     */
    @GetMapping("/by-resource")
    @SentinelResource(value = "byResource",blockHandler = "myHandleException",blockHandlerClass = MyBlockHandler.class)
    public CommonResponse<String> byResource(){
        log.info("coming in rate limit controller by resource");
        return CommonResponse.success();
    }


    /**
     * <h2>在 "触点链路" 中给URL添加流控规则</h2>
     * @return
     */
    @GetMapping("/by-url")
    @SentinelResource(value = "byUrl")
    public CommonResponse<String> byUrl(){
        log.info("coming in rate limit controller by Url");
        return CommonResponse.success("byUrl");
    }
}

添加流控规则,两个相同名称,一个是硬编码指定,一个是动态添加(保存JVM内存中)
在这里插入图片描述在这里插入图片描述

3.2 RestTemplate 降级

Sentinel支持对RestTemplate 服务调用进行保护,实现流控降级和异常降级

在这里插入图片描述

# 开启或关闭 @SentinelRestTemplate
resttemplate:
  sentinel:
    enabled: true

SentinelFallbackController :通过@SentinelResource配置异常处理

package cn.flowboot.e.commerce.controller;

import cn.flowboot.e.commerce.conf.RestTemplateExceptionHandler;
import cn.flowboot.e.commerce.fallback.MyFallbackHandler;
import cn.flowboot.e.commerce.vo.CommonResponse;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

/**
 * <h1>使用Sentinel保护RestTemplate服务调用</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/fallback")
public class SentinelFallbackController {

    //注入普通
    private final RestTemplate restTemplate;

    /**
     * <h2> remoteConsumer - <h2>
     * 容错降级:对于服务不可用时不能生效
     * version: 1.0 - 2022/3/18
     * @return {@link Map< String, Object> }
     */
    @GetMapping("/remote/consumer")
    @SentinelResource(
            value = "remoteConsumerFallback",
            fallback = "remoteConsumerFallback",
            fallbackClass = { MyFallbackHandler.class }
    )

    public CommonResponse remoteConsumer(){


        String requestUrL = "http://localhost:8500/sentinel-client/rest/remote/producer";
        log.info("RestTemplate request url [{}] ",requestUrL);
        return restTemplate.getForObject(requestUrL, CommonResponse.class);
    }

    /**
     * <h2>让 Sentinel 忽略一些异常</h2>
     * */
    @GetMapping("/ignore-exception")
    @SentinelResource(
            value = "ignoreException",
            fallback = "ignoreExceptionFallback",
            fallbackClass = { MyFallbackHandler.class },
            exceptionsToIgnore = { NullPointerException.class }
    )
    public CommonResponse ignoreException(@RequestParam(defaultValue = "1") Integer code) {

        if (code % 2 == 0) {
            throw new NullPointerException("yout input code is: " + code);
        } else if ( code % 3 == 0){
            throw new RuntimeException("yout input code is: " + code);
        }

        return CommonResponse.success();
    }

}

MyFallbackHandler

package cn.flowboot.e.commerce.fallback;

import cn.flowboot.e.commerce.vo.CommonResponse;
import lombok.extern.slf4j.Slf4j;

/**
 * <h1></h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@Slf4j
public class MyFallbackHandler {
    /**
     * <h2>remoteConsumer 方法的 fallback</h2>
     * */
    public static CommonResponse remoteConsumerFallback() {
        log.error("remote consumer service fallback");
        return CommonResponse.fail("fallback");
    }

    /**
     * <h2>ignoreException 方法的 fallback</h2>
     * */
    public static CommonResponse ignoreExceptionFallback(Integer code) {
        log.error("ignore exception input code: [{}] has trigger exception", code);
        return CommonResponse.fail("ignoreExceptionFallback");

    }

}

配置统一处理异常
RestTemplateExceptionHandler

package cn.flowboot.e.commerce.conf;

import cn.flowboot.e.commerce.vo.CommonResponse;
import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;

/**
 * <h1>RestTemplate 在限流或异常的兜底方法</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@Slf4j
public class RestTemplateExceptionHandler {

    /**
     * <h2> handleBlock - 限流处理方法<h2>
     * version: 1.0 - 2022/3/18
     * @param request 请求
     * @param body 数据
     * @param execution 请求链路
     * @param ex 限流处理方法
     * @return {@link SentinelClientHttpResponse }
     */
    public static SentinelClientHttpResponse handleBlock(HttpRequest request, byte[] body, ClientHttpRequestExecution execution,BlockException ex){
        log.error("handler restTemplate  block exception : [{}], [{}]", request.getURI().getPath(),request.getClass().getCanonicalName());
        return new SentinelClientHttpResponse(
                JSON.toJSONString(CommonResponse.fail("服务限流",request.getClass().getCanonicalName()))
        );
    }

    /**
     * <h2> handleFallback - 异常处理方法<h2>
     * version: 1.0 - 2022/3/18
     * @param request 请求
     * @param body 数据
     * @param execution 请求链路
     * @param ex 限流处理方法
     * @return {@link SentinelClientHttpResponse }
     */
    public static SentinelClientHttpResponse handleFallback(HttpRequest request, byte[] body, ClientHttpRequestExecution execution,BlockException ex){
        log.error("handler restTemplate  block exception : [{}], [{}]", request.getURI().getPath(),request.getClass().getCanonicalName());
        return new SentinelClientHttpResponse(
                JSON.toJSONString(CommonResponse.fail("服务异常",request.getClass().getCanonicalName()))
        );
    }

}

SentinelConfig

package cn.flowboot.e.commerce.conf;

import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * <h1>开启服务间的调用保护,需要给RestTemplate做一些包装</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@Configuration
public class SentinelConfig {

    /**
     * <h2> restTemplate - 包装RestTemplate<h2>
     * version: 1.0 - 2022/3/18
     * @param
     * @return {@link RestTemplate }
     */
    @Bean
    @SentinelRestTemplate(
            fallback = "handleFallback",fallbackClass = RestTemplateExceptionHandler.class,
            blockHandler = "handleBlock",blockHandlerClass = RestTemplateExceptionHandler.class
    )
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

SentinelRestTemplateController 使用统一配置的RestTemplate

package cn.flowboot.e.commerce.controller;

import cn.flowboot.e.commerce.block.handler.MyBlockHandler;
import cn.flowboot.e.commerce.dto.SearchGoodByIdsDto;
import cn.flowboot.e.commerce.vo.CommonResponse;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <h1>使用Sentinel保护RestTemplate服务调用</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/rest")
public class SentinelRestTemplateController {

    private final RestTemplate restTemplate;

    /**
     * <h2> remoteConsumer - <h2>
     * 容错降级:对于服务不可用时不能生效
     * version: 1.0 - 2022/3/18
     * @return {@link Map< String, Object> }
     */
    @GetMapping("/remote/consumer")
    public CommonResponse remoteConsumer(){


        String requestUrL = "http://localhost:8500/sentinel-client/rest/remote/producer";
        log.info("RestTemplate request url [{}] ",requestUrL);
        return restTemplate.getForObject(requestUrL, CommonResponse.class);
    }

    /**
     * <h2>在 dashboard 中“流控规则”中按照资源名称新增流控规则</h2>
     * @return
     */
    @GetMapping("/remote/producer")
    public CommonResponse<String> producer(){
        log.info("coming in rate limit controller by resource");
        return CommonResponse.success("producer");
    }
}

3.3 Feign 降级

@SentinelResource 中fallback、fallbackClass指定异常降级的类和方法Sentinel还对 Feign 实现了适配,支持Feign的容错降级

feign:
  sentinel:
    enabled: true

SentinelFeignController

package cn.flowboot.e.commerce.controller;

import cn.flowboot.e.commerce.fallback.MyFallbackHandler;
import cn.flowboot.e.commerce.feign.SentinelFeignClient;
import cn.flowboot.e.commerce.vo.CommonResponse;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

/**
 * <h1>使用Sentinel保护RestTemplate服务调用</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/feign")
public class SentinelFeignController {

    //注入普通
    private final SentinelFeignClient sentinelFeignClient;

    /**
     * <h2> remoteConsumer - <h2>
     * 容错降级:对于服务不可用时不能生效
     * version: 1.0 - 2022/3/18
     * @return {@link Map< String, Object> }
     */
    @GetMapping("/remote/consumer")
    public CommonResponse remoteConsumer(){
        log.info("Sentinel feign client request ");
        return sentinelFeignClient.producer();
    }

}

SentinelFeignClient 其中微服务定义不存在会抛出异常

package cn.flowboot.e.commerce.feign;

import cn.flowboot.e.commerce.feign.fallback.SentinelFeignClientFallback;
import cn.flowboot.e.commerce.vo.CommonResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * <h1>通过 Sentinel 对 OpenFeign 实现熔断降级</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@FeignClient(
        value = "e-commerce-sentinel-client-1",
        fallback = SentinelFeignClientFallback.class
)
public interface SentinelFeignClient {

    /**
     * <h2>在 dashboard 中“流控规则”中按照资源名称新增流控规则</h2>
     * @return
     */
    @GetMapping("/sentinel-client/rest/remote/producer")
    CommonResponse<String> producer();
}

SentinelFeignClientFallback Sentinel 对 OpenFeign 接口的降级策略

package cn.flowboot.e.commerce.feign.fallback;

import cn.flowboot.e.commerce.feign.SentinelFeignClient;
import cn.flowboot.e.commerce.vo.CommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * <h1>Sentinel 对 OpenFeign 接口的降级策略</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@Slf4j
@Component
public class SentinelFeignClientFallback implements SentinelFeignClient {

    /**
     * <h2>在 dashboard 中“流控规则”中按照资源名称新增流控规则</h2>
     *
     * @return
     */
    @Override
    public CommonResponse<String> producer() {
        return CommonResponse.fail("服务错误");
    }
}

4 Sentinel 存储

4.1 SpringCloud Alibaba Sentinel 结合 Nacos

  • Sentinel Dashboard将规则保存在内存中,重启之后就会丢失,所以,考虑使用外部持久化方案
  • 在Nacos中创建规则,Nacos会推送到客户端
  • Sentinel Dashboard也会从Nacos 去获取配置信息
  • Sentinel存储在 Nacos 中的限流数据结构

在这里插入图片描述
需要添加依赖(上述新建项目依赖包含)

<!-- Sentinel 使用 Nacos 存储规则 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

配置

spring:
  application:
    name: e-commerce-sentinel-client
  cloud:
    nacos:
      #服务发现
      discovery:
        enabled: true
        server-addr: 127.0.0.1:8848
        namespace: e-commerce-nacos-server
        metadata:
          management:
            context-path: ${server.servlet.context-path}/actuator
    sentinel:
      # 配置 sentinel dashboard 地址
      transport:
        dashboard: 127.0.0.1:7777
        port: 8719 # 会在应用对应的机器上启动一个 Http Server, 该 Server 会与 Sentinel 控制台做交互
      datasource:
        # 名称任意, 代表数据源
        ds:
          nacos:
            # NacosDataSourceProperties.java 中定义
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            dataId: ${spring.application.name}-sentinel
            namespace: ${spring.cloud.nacos.discovery.namespace}
            groupId: DEFAULT_GROUP
            data-type: json
            # 规则类型: com.alibaba.cloud.sentinel.datasource.RuleType
            # FlowRule 就是限流规则
            rule-type: flow
      # 服务启动直接建立心跳连接
      eager: true
# 开启或关闭 @SentinelRestTemplate
resttemplate:
  sentinel:
    enabled: true
feign:
  sentinel:
    enabled: true

nacos 中添加配置 e-commerce-sentinel-client-sentinel
在这里插入图片描述

[
    {
        "resource": "byResource",
        "limitApp": "default",
        "grade": 1,
        "count": 5,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

5 Gateway 结合 Sentinel

5.1 添加依赖

 <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- Sentinel 使用 Nacos 存储规则 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

5.2 硬编码配置

package cn.flowboot.e.commerce.config;



import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;


/**
 * <h1>Gateway 集成 Sentinel 实现限流</h1>
 *
 * @version 1.0
 * @author: Vincent Vic
 * @since: 2022/03/18
 */
@Slf4j
@RequiredArgsConstructor
@Configuration
public class SentinelGatewayConfiguration {

    /** 视图解析器 */
    private final List<ViewResolver> viewResolvers;
    /** HTTP 请求和响应数据的编解码配置 */
    private final ServerCodecConfigurer serverCodecConfigurer;


    /**
     * <h2>限流异常处理器, 限流异常出现时, 执行到这个 handler</h2>
     * */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // 默认会返回错误 message, code 429
        return new SentinelGatewayBlockExceptionHandler(
                this.viewResolvers,
                this.serverCodecConfigurer
        );
    }

    /**
     * <h2>限流过滤器, 是 Gateway 全局过滤器, 优先级定义为最高</h2>
     * */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * <h2>初始化限流规则</h2>
     * */
    @PostConstruct
    public void doInit() {

        log.info("---------------------------------------------------");

        // 加载网关限流规则
        log.info("load sentinel gateway rules (code define)");
        initGatewayRules();

        // 加载自定义限流异常处理器
        initBlockHandler();

        log.info("---------------------------------------------------");
    }

    /**
     * <h2>硬编码网关限流规则</h2>
     * */
    private void initGatewayRules() {

        Set<GatewayFlowRule> rules = new HashSet<>();

        GatewayFlowRule rule = new GatewayFlowRule();
        // 指定限流模式, 根据 route_id 做限流, 默认的模式
        rule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID);
        // 指定 route_id -> service id
        rule.setResource("e-commerce-nacos-client");
        // 按照 QPS 限流
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 统计窗口和限流阈值
        rule.setIntervalSec(3);
        rule.setCount(1);
        rules.add(rule);
        // 加载到网关中
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * <h2>自定义限流异常处理器</h2>
     * */
    private void initBlockHandler() {

        // 自定义 BlockRequestHandler
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange,
                                                      Throwable throwable) {

                log.error("------------- trigger gateway sentinel rule -------------");

                Map<String, String> result = new HashMap<>();
                result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
                result.put("message", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
                result.put("route", "e-commerce-nacos-client");

                return ServerResponse
                        .status(HttpStatus.TOO_MANY_REQUESTS)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(result));
            }
        };

        // 设置自定义限流异常处理器
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

SentinelGatewayConfiguration 修改为api分组

public class SentinelGatewayConfiguration {

    // 相同代码略...
    /**
     * <h2>硬编码网关限流规则</h2>
     * */
    private void initGatewayRules() {

        Set<GatewayFlowRule> rules = new HashSet<>();

//        GatewayFlowRule rule = new GatewayFlowRule();
// ... 注释之前单个规则
//        rules.add(rule);

        // 限流分组, Sentinel 先去找规则定义, 再去找规则中定义的分组
        rules.add(
                new GatewayFlowRule("nacos-client-api-1")
                        .setCount(3).setIntervalSec(60)
        );
        rules.add(
                new GatewayFlowRule("nacos-client-api-2")
                        .setCount(1).setIntervalSec(60)
        );

        // 加载到网关中
        GatewayRuleManager.loadRules(rules);

        // 加载限流分组
        initCustomizedApis();
    }

    /**
     * <h2>硬编码网关限流分组</h2>
     * 1. 最大限制 - 演示
     * 2. 具体的分组
     * */
    private void initCustomizedApis() {

        Set<ApiDefinition> definitions = new HashSet<>();

        // nacos-client-api 组, 最大的限制
        ApiDefinition api = new ApiDefinition("nacos-client-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // 模糊匹配 /ecommerce-nacos-client/ 及其子路径的所有请求
                    add(new ApiPathPredicateItem()
                            .setPattern("/ecommerce-nacos-client/**")
                            // 根据前缀匹配
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});

        // nacos-client-api-1 分组
        ApiDefinition api1 = new ApiDefinition("nacos-client-api-1")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem()
                            // 精确匹配 /n/service/instance/e-commerce-nacos-client
                            .setPattern("/s/nacos-client/search/service/instance"));
                }});

        // nacos-client-api-2 分组
        ApiDefinition api2 = new ApiDefinition("nacos-client-api-2")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem()
                            // 精确匹配 /imooc/ecommerce-nacos-client/nacos-client/project-config
                            .setPattern("/ecommerce-nacos-client" +
                                    "/nacos-client/project-config"));
                }});

        definitions.add(api1);
        definitions.add(api2);

        // 加载限流分组
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
}

5.3 JSON文件配置方式

注释掉SentinelGatewayConfiguration中的@PostConstruct注解

在项目资源文件新建如下两个文件:
gateway-flow-rule-api-sentinel.json

[
  {
    "apiName": "nacos-client-api",
    "predicateItems": [
      {
        "pattern": "/s/nacos-client/search/service/instance"
      },
      {
        "pattern": "/n/service/instance/**",
        "matchStrategy": 1
      }
    ]
  }
]

gateway-flow-rule-sentinel.json

[
  {
    "resource": "e-commerce-nacos-client",
    "resourceMode": 0,
    "count": 3,
    "intervalSec": 60
  },
  {
    "resource": "nacos-client-api",
    "resourceMode": 1,
    "count": 1,
    "intervalSec": 60
  }
]

注意:maven clean 当前网关子项目,防止文件在target不存在
通过本地文件方式 配置

spring:
  cloud:
    sentinel:
      eager: true
      transport:
        port: 8720
        dashboard: 127.0.0.1:7777
      datasource:
        # 通过本地文件方式, 基于服务级别的配置
        dsl.file:
          file: classpath:gateway-flow-rule-sentinel.json
          # 代表服务级别的限流, 一步步点进去看, 文件类型
          ruleType: gw-flow
        # 通过本地文件方式, 细粒度对指定 api 进行配置
        ds2.file:
          file: classpath:gateway-flow-rule-api-sentinel.json
          # 代表 API 分组, 一步步点进去看, 文件类型
          ruleType: gw-api-group

5.3 Nacos存储配置方式

spring:
  cloud:
    sentinel:
      eager: true
      transport:
        port: 8720
        dashboard: 127.0.0.1:7777
      datasource:
        # 集成 Nacos
        ds1:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            namespace: ${spring.cloud.nacos.discovery.namespace}
            # 测试时, 看看 Nacos 中修改是否能让 dashboard 生效, 就把第二个 count 也修改为 3
            data-id: gateway-flow-rule-sentinel
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: gw-flow
        ds2:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            namespace: ${spring.cloud.nacos.discovery.namespace}
            data-id: gateway-flow-rule-sentinel
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: gw-api-group

Nacos 配置添加
gateway-flow-rule-sentinel

[
  {
    "resource": "e-commerce-nacos-client",
    "resourceMode": 0,
    "count": 3,
    "intervalSec": 60
  },
  {
    "resource": "nacos-client-api",
    "resourceMode": 1,
    "count": 1,
    "intervalSec": 60
  }
]

gateway-flow-rule-api-sentinel

[
  {
    "apiName": "nacos-client-api",
    "predicateItems": [
      {
        "pattern": "/s/nacos-client/search/service/instance"
      },
      {
        "pattern": "/n/service/instance/**",
        "matchStrategy": 1
      }
    ]
  }
]

均为json,上述三种需要分开测试,还有其他存储方式

Spring Cloud Alibaba 学习笔记项目:Github,学习笔记,仅为组件学习,并没有完整案例项目

最后

以上就是爱笑冰淇淋最近收集整理的关于Spring Cloud Alibaba】Sentinel 流量控制1 搭建SpringCloud Alibaba Sentinel控制台2 项目集成3 降级4 Sentinel 存储5 Gateway 结合 Sentinel的全部内容,更多相关Spring内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(116)

评论列表共有 0 条评论

立即
投稿
返回
顶部