双剑合璧 Nacos 结合 Sentinel 实现流量安全控制

Alibaba Sentinel 是一款高性能且轻量级的流量控制、熔断降级解决方案。是面向分布式服务架构的高可用流量控制组件。

Sentinel 官网:https://sentinelguard.io/zh-cn/

Github:https://github.com/alibaba/Sentinel

1|0Sentinel 是什么

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来保障微服务的稳定性。

Sentinel 具有以下特征:

  • 「丰富的应用场景」:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 「完备的实时监控」:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 「广泛的开源生态」:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 「完善的 SPI 扩展点」:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 主要特征」

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdbaee5867b?w=1470&h=686&f=png&s=137125

Sentinel 开源生态」

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdbafd5c5dd?w=2516&h=1256&f=png&s=571318

Sentinel 目前已经针对 Servlet、Dubbo、Spring Boot/Spring Cloud、gRPC 等进行了适配,用户只需引入相应依赖并进行简单配置即可非常方便地享受 Sentinel 的高可用流量防护能力。Sentinel 还为 Service Mesh 提供了集群流量防护的能力。未来 Sentinel 还会对更多常用框架进行适配。

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

2|0Sentinel 的历史

  • 2012 年,Sentinel 诞生,主要功能为入口流量控制。
  • 2013-2017 年,Sentinel 在阿里巴巴集团内部迅速发展,成为基础技术模块,覆盖了所有的核心场景。Sentinel 也因此积累了大量的流量归整场景以及生产实践。
  • 2018 年,Sentinel 开源,并持续演进。
  • 2019 年,Sentinel 朝着多语言扩展的方向不断探索,推出 C++ 原生版本,同时针对 Service Mesh 场景也推出了 Envoy 集群流量控制支持,以解决 Service Mesh 架构下多语言限流的问题。
  • 2020 年,推出 Sentinel Go 版本,继续朝着云原生方向演进。

3|0Sentinel 核心

Sentinel 的使用可以分为两个部分:

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持(见 主流框架适配)。
  • 控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。

4|0Sentinel 控制台

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

官网文档:https://github.com/alibaba/Sentinel/wiki/控制台

4|1获取控制台

您可以从 release 页面 下载最新版本的控制台 jar 包。

您也可以从最新版本的源码自行构建 Sentinel 控制台:

  • 下载 控制台 工程
  • 使用以下命令将代码打包成一个 fat jar: mvn clean package

4|2启动控制台

启动命令如下,本文使用的是目前最新 1.7.2 版本:

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.2.jar

「注意」:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。

其中 -Dserver.port=8080 用于指定 Sentinel 控制台端口为 8080

从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的「登录」功能,默认用户名和密码都是 sentinel。可以参考 鉴权模块文档 配置用户名和密码。

注:若您的应用为 Spring Boot 或 Spring Cloud 应用,您可以通过 Spring 配置文件来指定配置,详情请参考 Spring Cloud Alibaba Sentinel 文档

为了方便启动,可以编写一个启动脚本 run.bat

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.2.jar

pause

4|3访问

访问:http://localhost:8080/

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdbb0db4acb?w=617&h=414&f=png&s=24829

输入默认用户名和密码 sentinel 点击登录。至此控制台就安装完成了。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdbb162b557?w=1386&h=687&f=png&s=40479

5|0环境准备

sentinel-demo 聚合工程。SpringBoot 2.3.0.RELEASESpring Cloud Hoxton.SR4

  • Nacos 注册中心
  • product-service:商品服务,提供了 /product/{id} 接口
  • order-service-rest:订单服务,基于 Ribbon 通过 RestTemplate 调用商品服务
  • order-server-feign:订单服务,基于 Feign 通过声明式服务调用商品服务

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdbb26369fe?w=1364&h=438&f=png&s=44004

6|0客户端接入控制台

控制台启动后,客户端需要按照以下步骤接入到控制台:

  • 添加依赖
  • 定义资源
  • 定义规则

先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。

由于我们的项目是 Spring Cloud 项目,所以可以借助官方文档来进行学习。

Spring 官网文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html

Github 文档:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

6|1添加依赖

父工程需要添加如下依赖:

<dependencyManagement>

    <dependencies>

        <dependency>

            <groupId>com.alibaba.cloud</groupId>

            <artifactId>spring-cloud-alibaba-dependencies</artifactId>

            <version>2.1.0.RELEASE</version>

            <type>pom</type>

            <scope>import</scope>

        </dependency>

    </dependencies>

</dependencyManagement>

子工程需要添加如下依赖:

<!-- spring cloud alibaba sentinel 依赖 -->

<dependency>

    <groupId>com.alibaba.cloud</groupId>

    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>

</dependency>

配置文件

客户端需要启动 Transport 模块来与 Sentinel 控制台进行通信。

order-service-rest 的 application.yml

spring:

  cloud:

    # 配置 Sentinel

    sentinel:

      transport:

        port: 8719

        dashboard: localhost:8080

这里的 spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一个限流规则,会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中。

初始化客户端

「确保客户端有访问量」,Sentinel 会在「客户端首次调用的时候」进行初始化,开始向控制台发送心跳包。

简单的理解就是:访问一次客户端,Sentinel 即可完成客户端初始化操作,并持续向控制台发送心跳包。

访问

多次访问:http://localhost:9090/order/1 然后查看控制台实时监控结果如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdc45a60ccb?w=1940&h=975&f=png&s=89864

6|2定义资源

「资源」 是 Sentinel 中的核心概念之一。我们说的资源,可以是任何东西,服务,服务里的方法,甚至是一段代码。最常用的资源是我们代码中的 Java 方法。Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

官网文档:https://github.com/alibaba/Sentinel/wiki/如何使用#定义资源

注解支持

官网文档:https://github.com/alibaba/Sentinel/wiki/注解支持

OrderServiceImpl.java

package com.example.service.impl;

 

import com.alibaba.csp.sentinel.annotation.SentinelResource;

import com.alibaba.csp.sentinel.slots.block.BlockException;

import com.example.pojo.Order;

import com.example.service.OrderService;

import com.example.service.ProductService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

 

import java.util.Arrays;

 

@Service

public class OrderServiceImpl implements OrderService {

 

    @Autowired

    private ProductService productService;

 

    /**

     * 根据主键和订单编号查询订单

     *

     * @param id

     * @param orderNo

     * @return

     */

    @Override

    @SentinelResource(value = "selectOrderByIdAndOrderNo",

            blockHandler = "selectOrderByIdAndOrderNoBlockHandler",

            fallback = "selectOrderByIdAndOrderNoFallback")

    public Order selectOrderByIdAndOrderNo(Integer id, String orderNo) {

        return new Order(id, orderNo, "中国", 2666D,

                Arrays.asList(productService.selectProductById(1)));

    }

 

    // 服务流量控制处理,参数最后多一个 BlockException,其余与原函数一致。

    public Order selectOrderByIdAndOrderNoBlockHandler(Integer id, String orderNo,

                                                       BlockException ex) {

        // Do some log here.

        ex.printStackTrace();

        return new Order(id, "服务流量控制处理-托底数据", "中国", 2666D,

                Arrays.asList(productService.selectProductById(1)));

    }

 

    // 服务熔断降级处理,函数签名与原函数一致或加一个 Throwable 类型的参数

    public Order selectOrderByIdAndOrderNoFallback(Integer id, String orderNo,

                                                   Throwable throwable) {

        System.out.println("order-service 服务的 selectOrderById 方法出现异常,异常信息如下:"

                + throwable);

        return new Order(id, "服务熔断降级处理-托底数据", "中国", 2666D,

                Arrays.asList(productService.selectProductById(1)));

    }

 

}

ProductServiceImpl.java

package com.example.service.impl;

 

import com.alibaba.csp.sentinel.annotation.SentinelResource;

import com.alibaba.csp.sentinel.slots.block.BlockException;

import com.example.pojo.Product;

import com.example.service.ProductService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.web.client.RestTemplate;

 

/**

 * 商品管理

 */

@Service

public class ProductServiceImpl implements ProductService {

 

    @Autowired

    private RestTemplate restTemplate;

 

    /**

     * 根据主键查询商品

     *

     * @param id

     * @return

     */

    @SentinelResource(value = "selectProductById",

            blockHandler = "selectProductByIdBlockHandler", fallback = "selectProductByIdFallback")

    @Override

    public Product selectProductById(Integer id) {

        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);

    }

 

    // 服务流量控制处理,参数最后多一个 BlockException,其余与原函数一致。

    public Product selectProductByIdBlockHandler(Integer id, BlockException ex) {

        // Do some log here.

        ex.printStackTrace();

        return new Product(id, "服务流量控制处理-托底数据", 1, 2666D);

    }

 

    // 服务熔断降级处理,函数签名与原函数一致或加一个 Throwable 类型的参数

    public Product selectProductByIdFallback(Integer id, Throwable throwable) {

        System.out.println("product-service 服务的 selectProductById 方法出现异常,异常信息如下:"

                + throwable);

        return new Product(id, "服务熔断降级处理-托底数据", 1, 2666D);

    }

 

}

注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT
  • blockHandler / blockHandlerClassblockHandler对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,「不能针对业务异常进行处理」

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallback 和 defaultFallback,则被限流降级时会将 BlockException 「直接抛出」(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

从 1.4.0 版本开始,注解方式定义资源支持自动统计业务异常,无需手动调用 Tracer.trace(ex) 来记录业务异常。Sentinel 1.4.0 以前的版本需要自行调用 Tracer.trace(ex) 来记录业务异常。

6|3定义规则

Sentinel 的所有规则都可以在「内存态中动态地查询及修改,修改之后立即生效」。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略。

Sentinel 支持以下几种规则:「流量控制规则」「熔断降级规则」「热点参数规则」「系统保护规则」「来源访问控制规则」

官网文档:https://github.com/alibaba/Sentinel/wiki/如何使用#规则的种类

流量控制规则

添加流量控制规则

选择 簇点链路 找到定义好的资源 selectProductById 并点击对应的规则按钮进行设置。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdc53300edf?w=1386&h=687&f=png&s=78699

比如我们设置一个流量控制规则,定义资源访问的 QPS 为 1(每秒能处理查询数目)。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdc5790998c?w=862&h=507&f=png&s=39449https://user-gold-cdn.xitu.io/2020/6/12/172a5fdcc1cde14a?w=1893&h=648&f=png&s=63276

测试

快速刷新页面多次访问:http://localhost:9090/order/idAndOrderNo?id=1&orderNo=order-001 结果如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdccdf63802?w=574&h=403&f=png&s=47693

熔断降级规则

模拟服务出错

修改 order-service-rest 项目中的核心代码,模拟服务出错。

package com.example.service.impl;

 

import com.alibaba.csp.sentinel.annotation.SentinelResource;

import com.alibaba.csp.sentinel.slots.block.BlockException;

import com.example.pojo.Product;

import com.example.service.ProductService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.web.client.RestTemplate;

 

/**

 * 商品管理

 */

@Service

public class ProductServiceImpl implements ProductService {

 

    @Autowired

    private RestTemplate restTemplate;

 

    /**

     * 根据主键查询商品

     *

     * @param id

     * @return

     */

    @SentinelResource(value = "selectProductById",

            blockHandler = "selectProductByIdBlockHandler", fallback = "selectProductByIdFallback")

    @Override

    public Product selectProductById(Integer id, String productName) {

        // 模拟查询主键为 1 的商品信息会导致异常

        if (1 == id)

            throw new RuntimeException("查询主键为 1 的商品信息导致异常");

        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);

    }

 

    // 服务流量控制处理,参数最后多一个 BlockException,其余与原函数一致。

    public Product selectProductByIdBlockHandler(Integer id, BlockException ex) {

        // Do some log here.

        ex.printStackTrace();

        return new Product(id, "服务流量控制处理-托底数据", 1, 2666D);

    }

 

    // 服务熔断降级处理,函数签名与原函数一致或加一个 Throwable 类型的参数

    public Product selectProductByIdFallback(Integer id, Throwable throwable) {

        System.out.println("product-service 服务的 selectProductById 方法出现异常,异常信息如下:"

                + throwable);

        return new Product(id, "服务熔断降级处理-托底数据", 1, 2666D);

    }

 

}

添加熔断降级规则

熔断降级规则支持相应时间、异常比例、异常数三种方式。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdd098faaf8?w=862&h=401&f=png&s=35108

测试

访问:http://localhost:9090/order/idAndOrderNo?id=1&orderNo=order-001 结果如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdd4c572b4c?w=559&h=394&f=png&s=48010

热点参数规则

热点参数规则是一种更细粒度的流控规则,它允许将规则具体到参数上。比如 selectOrderByIdAndOrderNo 方法有两个参数,我们对第一个参数进行限流,对第二个参数不限流。

添加热点参数规则

选择 簇点链路 找到定义好的资源 selectOrderByIdAndOrderNo 并点击对应的规则按钮进行设置。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdd4e16e99d?w=1386&h=687&f=png&s=78395

设置热点参数规则,定义对资源的第一个参数的 QPS 为 1(每秒能处理查询数目)。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdd644077c2?w=695&h=423&f=png&s=22758https://user-gold-cdn.xitu.io/2020/6/12/172a5fddb62bec34?w=1344&h=517&f=png&s=47792

测试

分别用两个参数访问,会发现只对第一个参数限流了。

快速刷新页面多次访问:http://localhost:9090/order/idAndOrderNo?id=1 被限流。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fde31f3417d?w=356&h=321&f=png&s=28726

快速刷新页面多次访问:http://localhost:9090/order/idAndOrderNo?orderNo=order-001 正常访问。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fde15ea1739?w=323&h=319&f=png&s=23082

授权规则

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

Sentinel 提供了 RequestOriginParser 接口来处理来源。一旦 Sentinel 保护的接口资源被访问,Sentinel 就会调用 RequestOriginParser 的实现类去解析访问来源。

自定义来源处理规则

package com.example.sentinel;

 

import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;

import org.springframework.stereotype.Component;

 

import javax.servlet.http.HttpServletRequest;

 

/**

 * 自定义来源处理规则

 */

@Component

public class MyRequestOriginParser implements RequestOriginParser {

 

    @Override

    public String parseOrigin(HttpServletRequest request) {

        return request.getParameter("userName");

    }

 

}

新增授权规则

下图配置的意思是资源 selectOrderByIdAndOrderNo 只有 userName=zhangsan 的用户无法访问(黑名单)

https://user-gold-cdn.xitu.io/2020/6/12/172a5fde543dea06?w=1378&h=580&f=png&s=61309

测试

快速刷新页面多次访问:http://localhost:9090/order/idAndOrderNo?id=1&userName=zhangsan 被限流。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fde31f3417d?w=356&h=321&f=png&s=28726

快速刷新页面多次访问:http://localhost:9090/order/idAndOrderNo?id=1&userName=lisi 正常访问。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fde15ea1739?w=323&h=319&f=png&s=23082

系统保护规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 LOAD、RT、线程数、入口 QPS 和 CPU 使用率五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量(进入应用的流量)生效。

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
  • CPU使用率:当单台机器上所有入口流量的 CPU 使用率达到阈值即触发系统保护。

动态规则扩展

官网文档:

SentinelProperties 内部提供了 TreeMap 类型的 datasource 属性用于配置数据源信息。支持:

  • 文件配置规则
  • Nacos 配置规则
  • ZooKeeper 配置规则
  • Apollo 配置规则
  • Redis 配置规则

文件配置规则

Sentinel 支持通过本地文件加载规则配置,使用方式如下(限流规则作为演示):

spring:

  cloud:

    # 配置 Sentinel

    sentinel:

      datasource:

        ds1:

          file:

            file: classpath:flowRule.json

            data-type: json

            rule-type: flow

flowRule.json 对应 com.alibaba.csp.sentinel.slots.block.RuleConstant 各属性。

[

  {

    "resource": "selectProductList",

    "count": 1,

    "grade": 1,

    "limitApp": "default",

    "strategy": 0,

    "controlBehavior": 0

  }

]

重要属性:

Field

说明

默认值

resource

资源名,资源名是限流规则的作用对象

 

count

限流阈值

 

grade

限流阈值类型,QPS 模式(1)或并发线程数模式(0)

QPS 模式

limitApp

流控针对的调用来源

default,代表不区分调用来源

strategy

调用关系限流策略:直接、链路、关联

根据资源本身(直接)

controlBehavior

流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流

直接拒绝

clusterMode

是否集群限流

访问客户端以后,刷新控制台,查看流控规则如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fde610d8e8d?w=1884&h=639&f=png&s=63513

7|0RestTemplate 支持

Spring Cloud Alibaba Sentinel 支持对 RestTemplate 调用的服务进行服务保护。需要在构造 RestTemplate Bean 时添加 @SentinelRestTemplate 注解。

7|1启动类

OrderServiceRestApplication.java

package com.example;

 

import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;

import com.example.exception.ExceptionUtil;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.context.annotation.Bean;

import org.springframework.web.client.RestTemplate;

 

@SpringBootApplication

public class OrderServiceRestApplication {

 

    @Bean

    @LoadBalanced

    @SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class,

            fallback = "fallback", fallbackClass = ExceptionUtil.class)

    public RestTemplate restTemplate() {

        return new RestTemplate();

    }

 

    public static void main(String[] args) {

        SpringApplication.run(OrderServiceRestApplication.class, args);

    }

 

}

7|2服务熔断处理类

ExceptionUtil.java 必须使用静态方法。

package com.example.exception;

 

import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;

import com.alibaba.csp.sentinel.slots.block.BlockException;

import com.alibaba.fastjson.JSON;

import com.example.pojo.Product;

import org.springframework.http.HttpRequest;

import org.springframework.http.client.ClientHttpRequestExecution;

import org.springframework.http.client.ClientHttpResponse;

 

public class ExceptionUtil {

 

    // 服务流量控制处理

    public static ClientHttpResponse handleException(HttpRequest request,

                                                     byte[] body,

                                                     ClientHttpRequestExecution execution,

                                                     BlockException exception) {

        exception.printStackTrace();

        return new SentinelClientHttpResponse(

                JSON.toJSONString(new Product(1, "服务流量控制处理-托底数据", 1, 2666D)));

    }

 

    // 服务熔断降级处理

    public static ClientHttpResponse fallback(HttpRequest request,

                                                    byte[] body,

                                                    ClientHttpRequestExecution execution,

                                                    BlockException exception) {

        exception.printStackTrace();

        return new SentinelClientHttpResponse(

                JSON.toJSONString(new Product(1, "服务熔断降级处理-托底数据", 1, 2666D)));

    }

 

}

7|3访问

控制台设置流量控制规则,定义资源访问的 QPS 为 1(每秒能处理查询数目)。

快速刷新页面多次访问:http://localhost:9090/order/1 结果如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdccdf63802?w=574&h=403&f=png&s=47693

8|0OpenFeign 支持

8|1添加依赖

<!-- spring cloud alibaba sentinel 依赖 -->

<dependency>

    <groupId>com.alibaba.cloud</groupId>

    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>

</dependency>

<!-- spring cloud openfeign 依赖 -->

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-openfeign</artifactId>

</dependency>

8|2开启 Sentinel

server:

  port: 9091 # 端口

 

spring:

  application:

    name: order-service-feign # 应用名称

  cloud:

    # 配置 Nacos 注册中心

    nacos:

      discovery:

        enabled: true # 如果不想使用 Nacos 进行服务注册和发现,设置为 false 即可

        server-addr: 127.0.0.1:8848 # Nacos 服务器地址

    # 配置 Sentinel

    sentinel:

      transport:

        port: 8719

        dashboard: localhost:8080

 

# feign 开启 sentinel 支持

feign:

  sentinel:

    enabled: true

8|3熔断降级

ProductServiceFallback.java

package com.example.fallback;

 

import com.example.pojo.Product;

import com.example.service.ProductService;

import feign.hystrix.FallbackFactory;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;

 

/**

 * 服务熔断降级处理可以捕获异常

 */

@Slf4j

@Component

public class ProductServiceFallbackFactory implements FallbackFactory<ProductService> {

 

    @Override

    public ProductService create(Throwable throwable) {

        return new ProductService() {

            @Override

            public Product selectProductById(Integer id) {

                // 获取日志,在需要捕获异常的方法中进行处理

                log.error("product-service 服务的 selectProductById 方法出现异常,异常信息如下:"

                        + throwable);

                return new Product(id, "托底数据", 1, 2666D);

            }

        };

    }

 

}

8|4消费服务

ProductService.java

package com.example.service;

 

import com.example.fallback.ProductServiceFallbackFactory;

import com.example.pojo.Product;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

 

// 声明需要调用的服务

@FeignClient(value = "product-service", fallbackFactory = ProductServiceFallbackFactory.class)

public interface ProductService {

 

    /**

     * 根据主键查询商品

     *

     * @param id

     * @return

     */

    @GetMapping("/product/{id}")

    Product selectProductById(@PathVariable("id") Integer id);

 

}

OrderServiceImpl.java

package com.example.service.impl;

 

import com.example.pojo.Order;

import com.example.service.OrderService;

import com.example.service.ProductService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

 

import java.util.Arrays;

 

@Service

public class OrderServiceImpl implements OrderService {

 

    @Autowired

    private ProductService productService;

 

    /**

     * 根据主键查询订单

     *

     * @param id

     * @return

     */

    @Override

    public Order selectOrderById(Integer id) {

        return new Order(id, "order-001", "中国", 2666D,

                Arrays.asList(productService.selectProductById(1)));

    }

 

}

8|5控制层

package com.example.controller;

 

import com.example.pojo.Order;

import com.example.service.OrderService;

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;

 

    /**

     * 根据主键查询订单

     *

     * @param id

     * @return

     */

    @GetMapping("/{id}")

    public Order selectOrderById(@PathVariable("id") Integer id) {

        return orderService.selectOrderById(id);

    }

 

}

8|6启动类

package com.example;

 

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.openfeign.EnableFeignClients;

 

// 开启 FeignClients 注解

@EnableFeignClients

// 开启 @EnableDiscoveryClient 注解,当前版本默认会开启该注解

//@EnableDiscoveryClient

@SpringBootApplication

public class OrderServiceFeignApplication {

 

    public static void main(String[] args) {

        SpringApplication.run(OrderServiceFeignApplication.class, args);

    }

 

}

8|7测试

控制台信息如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fde90287da6?w=1886&h=711&f=png&s=84585

添加流量控制规则,定义资源访问的 QPS 为 1(每秒能处理查询数目)。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdee93aa278?w=862&h=499&f=png&s=40603

快速刷新页面多次访问:http://localhost:9091/order/1 结果如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdf013bbe26?w=439&h=397&f=png&s=41842

或者关闭服务提供者,访问:http://localhost:9091/order/1 结果如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdf013bbe26?w=439&h=397&f=png&s=41842

9|0Gateway 支持

Sentinel 支持对 Spring Cloud Gateway、Netflix Zuul 等主流的 API Gateway 进行限流。

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdf5264dd0c?w=1216&h=756&f=png&s=175928

官网文档:

9|1创建项目

创建 gateway-server-sentinel 项目。

9|2添加依赖

单独使用添加 sentinel-spring-cloud-gateway-adapter 依赖即可。

若想跟 Sentinel Starter 配合使用,需要加上 spring-cloud-alibaba-sentinel-gateway 依赖来让 spring-cloud-alibaba-sentinel-gateway 模块里的 Spring Cloud Gateway 自动化配置类生效。

同时请将 spring.cloud.sentinel.filter.enabled 配置项置为 false(若在网关流控控制台上看到了 URL 资源,就是此配置项没有置为 false)。

<?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>gateway-demo</artifactId>

        <groupId>com.example</groupId>

        <version>1.0-SNAPSHOT</version>

    </parent>

    <modelVersion>4.0.0</modelVersion>

 

    <artifactId>gateway-server-sentinel</artifactId>

 

    <!-- 项目依赖 -->

    <dependencies>

        <!-- spring cloud gateway 依赖 -->

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-gateway</artifactId>

        </dependency>

        <!-- spring cloud alibaba nacos discovery 依赖 -->

        <dependency>

            <groupId>com.alibaba.cloud</groupId>

            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

        </dependency>

        <!-- 单独使用 -->

        <!-- sentinel gateway adapter 依赖 -->

        <dependency>

            <groupId>com.alibaba.csp</groupId>

            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>

        </dependency>

        <!-- 和 Sentinel Starter 配合使用 -->

        <!--

        <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>

        -->

    </dependencies>

 

</project>

9|3配置文件

server:

  port: 9001 # 端口

 

spring:

  application:

    name: gateway-server-sentinel # 应用名称

  cloud:

    sentinel:

      filter:

        enabled: false

    gateway:

      discovery:

        locator:

          # 是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例。

          enabled: true                  # 是否开启基于服务发现的路由规则

          lower-case-service-id: true    # 是否将服务名称转小写

      # 路由规则

      routes:

        - id: order-service           # 路由 ID,唯一

          uri: lb://order-service     # 目标 URI,lb:// 根据服务名称从注册中心获取服务请求地址

          predicates:                 # 断言(判断条件)

            # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后

            - Path=/order/**

9|4限流规则配置类

使用时只需注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。

GatewayConfiguration.java

package com.example.config;

 

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.exception.SentinelGatewayBlockExceptionHandler;

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.codec.ServerCodecConfigurer;

import org.springframework.web.reactive.result.view.ViewResolver;

 

import javax.annotation.PostConstruct;

import java.util.Collections;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

 

/**

 * 限流规则配置类

 */

@Configuration

public class GatewayConfiguration {

 

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

 

    /**

     * 构造器

     *

     * @param viewResolversProvider

     * @param serverCodecConfigurer

     */

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,

                                ServerCodecConfigurer serverCodecConfigurer) {

        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);

        this.serverCodecConfigurer = serverCodecConfigurer;

    }

 

    /**

     * 限流异常处理器

     *

     * @return

     */

    @Bean

    @Order(Ordered.HIGHEST_PRECEDENCE)

    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {

        // Register the block exception handler for Spring Cloud Gateway.

        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);

    }

 

    /**

     * 限流过滤器

     *

     * @return

     */

    @Bean

    @Order(Ordered.HIGHEST_PRECEDENCE)

    public GlobalFilter sentinelGatewayFilter() {

        return new SentinelGatewayFilter();

    }

 

    /**

     * Spring 容器初始化的时候执行该方法

     */

    @PostConstruct

    public void doInit() {

        // 加载网关限流规则

        initGatewayRules();

    }

 

    /**

     * 网关限流规则

     */

    private void initGatewayRules() {

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

        /*

            resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称

            count:限流阈值

            intervalSec:统计时间窗口,单位是秒,默认是 1 秒

         */

        rules.add(new GatewayFlowRule("order-service")

                .setCount(3) // 限流阈值

                .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒

        // 加载网关限流规则

        GatewayRuleManager.loadRules(rules);

    }

 

}

9|5启动类

GatewayServerSentinelApplication.java

package com.example;

 

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

 

// 开启 EurekaClient 注解,目前版本如果配置了 Eureka 注册中心,默认会开启该注解

//@EnableEurekaClient

@SpringBootApplication

public class GatewayServerSentinelApplication {

 

    public static void main(String[] args) {

        SpringApplication.run(GatewayServerSentinelApplication.class, args);

    }

 

}

9|6访问

多次访问:http://localhost:9001/order/1 结果如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdf55e07e92?w=353&h=65&f=png&s=1679

接口 BlockRequestHandler 的默认实现为 DefaultBlockRequestHandler,当触发限流时会返回默认的错误信息:Blocked by Sentinel: FlowException。我们可以通过 GatewayCallbackManager 定制异常提示信息。

9|7自定义异常提示

GatewayCallbackManager 的 setBlockHandler 注册函数用于实现自定义的逻辑,处理被限流的请求。

package com.example.config;

 

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 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.*;

 

/**

 * 限流规则配置类

 */

@Configuration

public class GatewayConfiguration {

 

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

 

    /**

     * 构造器

     *

     * @param viewResolversProvider

     * @param serverCodecConfigurer

     */

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,

                                ServerCodecConfigurer serverCodecConfigurer) {

        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);

        this.serverCodecConfigurer = serverCodecConfigurer;

    }

 

    /**

     * 限流异常处理器

     *

     * @return

     */

    @Bean

    @Order(Ordered.HIGHEST_PRECEDENCE)

    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {

        // Register the block exception handler for Spring Cloud Gateway.

        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);

    }

 

    /**

     * 限流过滤器

     *

     * @return

     */

    @Bean

    @Order(Ordered.HIGHEST_PRECEDENCE)

    public GlobalFilter sentinelGatewayFilter() {

        return new SentinelGatewayFilter();

    }

 

    /**

     * Spring 容器初始化的时候执行该方法

     */

    @PostConstruct

    public void doInit() {

        // 加载网关限流规则

        initGatewayRules();

        // 加载自定义限流异常处理器

        initBlockHandler();

    }

 

    /**

     * 网关限流规则

     */

    private void initGatewayRules() {

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

        /*

            resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称

            count:限流阈值

            intervalSec:统计时间窗口,单位是秒,默认是 1 秒

         */

        rules.add(new GatewayFlowRule("order-service")

                .setCount(3) // 限流阈值

                .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒

        // 加载网关限流规则

        GatewayRuleManager.loadRules(rules);

    }

 

    /**

     * 自定义限流异常处理器

     */

    private void initBlockHandler() {

        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {

            @Override

            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {

                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", "order-service");

                return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)

                        .contentType(MediaType.APPLICATION_JSON)

                        .body(BodyInserters.fromValue(result));

            }

        };

 

        // 加载自定义限流异常处理器

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);

    }

 

}

「访问」

多次访问:http://localhost:9001/order/1 结果如下:

https://user-gold-cdn.xitu.io/2020/6/12/172a5fdf7092ed48?w=402&h=180&f=png&s=19807

9|8分组限流

package com.example.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 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.*;

 

/**

 * 限流规则配置类

 */

@Configuration

public class GatewayConfiguration {

 

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

 

    /**

     * 构造器

     *

     * @param viewResolversProvider

     * @param serverCodecConfigurer

     */

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,

                                ServerCodecConfigurer serverCodecConfigurer) {

        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);

        this.serverCodecConfigurer = serverCodecConfigurer;

    }

 

    /**

     * 限流异常处理器

     *

     * @return

     */

    @Bean

    @Order(Ordered.HIGHEST_PRECEDENCE)

    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {

        // Register the block exception handler for Spring Cloud Gateway.

        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);

    }

 

    /**

     * 限流过滤器

     *

     * @return

     */

    @Bean

    @Order(Ordered.HIGHEST_PRECEDENCE)

    public GlobalFilter sentinelGatewayFilter() {

        return new SentinelGatewayFilter();

    }

 

    /**

     * Spring 容器初始化的时候执行该方法

     */

    @PostConstruct

    public void doInit() {

        // 加载网关限流规则

        initGatewayRules();

        // 加载自定义限流异常处理器

        initBlockHandler();

    }

 

    /**

     * 网关限流规则

     */

    private void initGatewayRules() {

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

        /*

            resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称

            count:限流阈值

            intervalSec:统计时间窗口,单位是秒,默认是 1 秒

         */

        // rules.add(new GatewayFlowRule("order-service")

        //         .setCount(3) // 限流阈值

        //         .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒

        // --------------------限流分组----------start----------

        rules.add(new GatewayFlowRule("product-api")

                .setCount(3) // 限流阈值

                .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒

        rules.add(new GatewayFlowRule("order-api")

                .setCount(5) // 限流阈值

                .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒

        // --------------------限流分组-----------end-----------

        // 加载网关限流规则

        GatewayRuleManager.loadRules(rules);

        // 加载限流分组

        initCustomizedApis();

    }

 

    /**

     * 自定义限流异常处理器

     */

    private void initBlockHandler() {

        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {

            @Override

            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {

                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", "order-service");

                return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)

                        .contentType(MediaType.APPLICATION_JSON)

                        .body(BodyInserters.fromValue(result));

            }

        };

 

        // 加载自定义限流异常处理器

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);

    }

 

    /**

     * 限流分组

     */

    private void initCustomizedApis() {

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

        // product-api

        ApiDefinition api1 = new ApiDefinition("product-api")

                .setPredicateItems(new HashSet<ApiPredicateItem>() {{

                    // 匹配 /product-service/product 以及其子路径的所有请求

                    add(new ApiPathPredicateItem().setPattern("/product-service/product/**")

                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));

                }});

 

        // order-api

        ApiDefinition api2 = new ApiDefinition("order-api")

                .setPredicateItems(new HashSet<ApiPredicateItem>() {{

                    // 只匹配 /order-service/order/index

                    add(new ApiPathPredicateItem().setPattern("/order-service/order/index"));

                }});

        definitions.add(api1);

        definitions.add(api2);

        // 加载限流分组

        GatewayApiDefinitionManager.loadApiDefinitions(definitions);

    }

 

}

「访问」

访问:http://localhost:9001/product-service/product/1 「触发限流」

访问:http://localhost:9001/order-service/order/index 「触发限流」

访问:http://localhost:9001/order-service/order/1 「不会触发限流」

至此 Sentinel 服务哨兵知识点就讲解结束了。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nacos 可以与 Sentinel 集成,以实现动态的流量控制和熔断降级策略。这样,你可以使用 Nacos 配置中心来配置 Sentinel 规则,实现对应用程序的实时流量控制和熔断降级。 以下是集成 NacosSentinel 的基本步骤: 1. 首先,你需要在应用程序中引入 NacosSentinel 的客户端库。根据你使用的编程语言和框架,可能需要添加相应的依赖或库文件。 2. 在 Nacos 配置中心中创建配置项,用于存储 Sentinel 的规则配置。可以在 Nacos 控制台中手动创建或使用 API 自动创建。 3. 在应用程序中使用 Nacos 的配置读取 API,读取 Sentinel 规则配置。这些规则可以是流量控制规则、熔断降级规则等。 4. 将读取到的规则配置传递给 Sentinel 客户端,使其生效。这样,Sentinel 就会根据配置的规则对流量进行控制和熔断降级。 5. 在 Nacos 配置中心中更新规则配置时,应用程序会自动获取最新的配置,并且 Sentinel 会根据新的配置进行动态调整。 下面是一个示例伪代码,展示了如何集成 NacosSentinel: ```python import nacos import sentinel # 创建 Nacos 客户端实例 client = nacos.NacosClient(server_addresses='nacos-server:8848') # 从 Nacos 获取 Sentinel 规则配置 config = client.get_config(data_id='sentinel-rules', group='your-group') # 将规则配置传递给 Sentinel 客户端 sentinel.load_rules(config) # 应用程序主逻辑 def main(): # 执行应用程序逻辑 do_something() if __name__ == '__main__': main() ``` 在上面的示例中,`client.get_config()` 方法用于从 Nacos 中获取 Sentinel 规则配置。然后,通过 `sentinel.load_rules()` 方法将规则配置传递给 Sentinel 客户端。 通过上述步骤,你可以实现 NacosSentinel 的集成,实现动态的流量控制和熔断降级策略。在 Nacos 配置中心更新规则配置时,应用程序会自动获取最新的配置,并且 Sentinel 会根据新的配置进行动态调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值