Sentinel的基本概念与使用(一)

一、概述

1.1 在实际开发过程中,能用起来
1.2 常遇到的一些问题解答
1.2.1 sentinel是什么?
1.2.2 sentinel类似的产品有哪些?hystrix
1.2.3 限流算法?优点? 缺点?
1.2.4 sentinel默认的限流算法?特性?滑动窗口
1.2.5 sentinel能解决突发流量吗?不能,为什么? 因为它底层是滑动窗口
1.2.6 sentinel的阀值应用类型? QPS, 线程数,调用关系
1.2.7 sentinel可以做集群限流吗? 可以
1.2.8 Sentinel限流规则中有哪些限流模式? 直连,关联,链路

1.3 Sentinel是什么
【官网】: sentinelguard.io/zh-cn/

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

1.4 Sentinel基本概念?

资源:(sentinel都是针对资源进行限流, 资源可以是方法名)
资源是Sentinel的关键概念。它可以是java应用程序中任何内容(例如,由应用程序提供的服务, 或由应用程序调用其他应用提供的服务,甚至可以是一段代码。)
只要通过Sentinel API 定义的代码,就是资源,能够被Sentinel保护起来,大部分情况下,可以使用方法签名URL,甚至服务名称作为资源名来标识资源
规则: 围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整;

二、sentinel使用和限流规则

2.1 限流

2.1.1 为什么要限流??

微服务侧重两个维度:高可用、数据一致性

保证高可用措施:

  1. 缓存
  2. 降级(将某个服务业务功能简单化) 举例:一个请求过来,处理链路多且复杂,遇到服务器
    宕机,不能直接返回给客户看不懂的信息,给出
    友好的错误提示;比如try-catch()就可以实现降级
  3. 限流:在某一个时间段内只允许指定数量的请求过来, 与熔断降级(hystrix, 当请求量特别大的时候,可能会造成服务的宕机)需要区分清楚
2.2 限流算法
  • 计数器(固定窗口)

思想:计数器算法是使用计数器在周期内累加访问次数, 当达到设置的限流值时, 触发限流策略; 下一个周期开始时,进行清零, 重新计数。

此算法实现起来不管是单机环境还是分布式环境都是非常的简单,使用redis的incr原子自增性和线程安全即可轻松实现;
在这里插入图片描述
如上图所示:1分钟内限制100个请求,超过则触发限流策略

问题: 它无法解决临界问题(假设1分钟,1分钟之内阈值100,若前0:59秒之内没有什么流量,但是最后1秒钟突发100个请求, 过了1分钟之后清零,后面1:00-2:00前1秒钟突发100个请求),也就是2秒钟来了200个请求,这对服务器压力会非常的大(因为我的系统可能能承受2秒之内200个请求,但是如果这两百个请求都集中在2秒钟之内,则我的系统可能就承受不住);

  • 滑动窗口(sentinel限流算法默认的算法)
    它是在固定窗口之上的优化

思想: 滑动窗口算法是将时间周期分为N个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期

如下图,假设时间周期为1min, 将1min再分为2个小周期,统计每个小周期的访问数量,则可以看到,第一个时间周期内,访问量为75, 第二个时间周期内,访问数量为100, 超过100的访问则被限流掉了;
在这里插入图片描述
此算法就可以解决临界问题(因为滑动窗口,它根据时间滑动删除过期的小周期,比如上图中的0:00-0:30, 而只看0:30-current time时间范围之内是否请求数是否超过最大请求数100)

  • 漏桶算法
    跟令牌算法刚好相反, 设计思想跟阻塞队列一致
    在这里插入图片描述
    **解析:**上游可以不限制流量,设置漏桶的大小,当请求数超过漏桶大小时溢出(拒绝请求),而消费端以匀速消费;

思想:漏桶算法是首先想象有一个木桶,桶的容积是固定的,当有请求到来时,先放到木桶中,处理请求的worker以固定的速度从木桶中取出请求进行处理。
若请求量太大超过木桶的容积,则直接返回请求频率超限的错误码或者页面
问题: 木桶流入请求的速率是不固定的,但是流出的速率是恒定的。这样的话能保护系统资源不被打满,但是面对突发流量时会有大量请求失败,不适合电商抢购和微博出现热点事件等场景的限流;

  • 令牌桶算法
    跟漏桶算法刚好相反
    在这里插入图片描述

解析: 从客户端拿到的请求需要拿到令牌才会去处理请求,请求流量是可以控制(每次请求都需要拿到令牌token),但是处理请求的速率不会控制(开启多少个线程去消费取决于使用方)
思想:令牌桶是反向的“漏桶”, 它是以恒定的速度往木桶里面加入令牌,木桶满了则不再加入令牌。服务收到请求时尝试从木桶中取出一个令牌,如果能够得到令牌则继续执行后续的业务逻辑,如果没有得到令牌,等待获取token,不继续执行后续的业务逻辑;

能解决突发流量问题

三、限流框架基本使用

guava底层用的是令牌桶算法

前置一段代码热身,如下:

 <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>22.0</version>
        </dependency>
/**
 * 令牌桶算法
 */
public class RateLimiterDemo {

    private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /*线程数*/
    private static final int THREAD_COUNT = 25;

    public static void main(String[] args) {
        RateLimiterDemo rateLimiterDemo = new RateLimiterDemo();
        rateLimiterDemo.testRateLimiter1();
    }
    public void testRateLimiter1(){
        /*令牌桶算法  TPS:1*/
        RateLimiter rateLimiter = RateLimiter.create(1);
        Thread[] ts = new Thread[THREAD_COUNT];

        for (int i = 0; i < THREAD_COUNT; i++){
            ts[i] = new Thread(new RateLimiterThread(rateLimiter), "rateLimitThread-");
        }
        for (int i = 0; i < THREAD_COUNT; i++){
            ts[i].start();
        }
    }

    public class RateLimiterThread implements Runnable{

        private RateLimiter rateLimiter;

        public RateLimiterThread(RateLimiter rateLimiter){
            this.rateLimiter = rateLimiter;
        }
        @Override
        public void run() {
            rateLimiter.acquire(1);
            System.out.println(Thread.currentThread().getName() + "获取到了令牌,时间 = "+ FORMATTER.format(new Date()));
        }
    }

}

演示结果:
在这里插入图片描述
上述是使用了令牌桶对其进行限流的控制

3.1、Sentinel控制台

【参见】https://sentinelguard.io/zh-cn/docs/dashboard.html
在这里插入图片描述

这里下载:sentinel-dashboard-1.8.0.jar

在这里插入图片描述

  • 启动服务

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

在这里插入图片描述

  • 操作过程阐述

在使用sentinel的时候, 首先你要初始化sentinel的流控规则 ;

  1. 首先设置资源名
  2. 设置限流规则:QPS、线程数
  3. 设置阈值
  4. 设置控制策略
  5. 针对具体的客户端进行限流
  6. 流控模型
    API操作则一定要通过try{}catch(Exception){} 才能实现限流
3.2、Sentinel流控规则设置
3.2.1 流控规则设置1

流控规则设置代码演示:

  • OrderController
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class OrderController {

    @Autowired
    private OrderQueryService orderQueryService;

    @RequestMapping("/getOrder1")
    public String queryOrder1(@RequestParam("orderId") String orderId){
        return orderQueryService.queryOrderInfo(orderId);
    }

    /**
     * 限流实现方式一:抛出异常的方式定义资源
     * @param orderId
     * @return
     */
    @RequestMapping("/getOrder2")
    public String queryOrder2(@RequestParam("orderId") String orderId){
        Entry entry = null;
        /*资源名*/
        String resourceName = "zyping";
        try{
            /*entry 可以理解成入口登记,这一步就进行限流*/
            entry = SphU.entry(resourceName);
            /*被保护的逻辑, 这里为订单查询接口*/
            return orderQueryService.queryOrderInfo(orderId);
        }catch (BlockException blockException){
            /*接口被限流的时候,会进入到这里*/
            blockException.printStackTrace();
            return "接口限流,返回空";
        }finally {
            //SphU.entry(xxx) 需要与 entry.exit() 成对出现,否则会导致调用链记录异常
            if (entry != null){
                entry.exit();
            }

        }
    }

    /**
     * 限流实现方式二:注解定义资源
     * @param orderId
     * @return
     */
    @RequestMapping("/getOrder3")
    public String queryOrder3(@RequestParam("orderId") String orderId){
        return orderQueryService.queryOrderInfo3(orderId);

    }
}
  • OrderQueryService
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Component;

@Component
public class OrderQueryService {
    public String queryOrderInfo(String orderId) {

        System.out.println("获取订单信息:"+ orderId);
        return "return OrderInfo : "+ orderId;
    }

    /**
     * 订单查询接口, 使用Sentinel 注解实现限流
     * @param orderId
     * @SentinelResource的value是资源名
    * @return
     */
    @SentinelResource(value = "queryOrderInfo3", blockHandler = "handleFlowQpsException", fallback = "queryOrderInfo3Fallback")
    public String queryOrderInfo3(String orderId){
        /*模拟接口运行时抛出代码异常*/
        if ("000".equals(orderId)){
            throw new RuntimeException();
        }
        System.out.println("获取订单信息:" + orderId);
        return "return OrderInfo3 :" + orderId;
    }
    /**
     * 订单查询接口抛出限流或降级时的处理逻辑
     *
     * 注意:方法参数、返回值要与原函数保持一致
     * @return
     */
    public String handleFlowQpsException(String orderId, BlockException e){
        e.printStackTrace();
        return "handleFlowQpsException for queryOrderInfo3: "+ orderId;
    }

    /**
     * 订单查询接口运行时抛出的异常提供fallback处理
     *
     * 注意: 方法参数、返回值要与原函数保持一致
     * @return
     */
    public String queryOrderInfo3Fallback(String orderId, Throwable e){
        e.printStackTrace();
        return "queryOrderInfo3Fallback for queryOrderInfo3: "+ orderId;
    }

}
  • SpringBootSentinelApplication
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 org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.ArrayList;
import java.util.List;

@SpringBootApplication
public class SpringBootSentinelApplication {

    public static void main(String[] args) {
        /*启动时初始化流控规则*/
        initFlowQpsRule();
        SpringApplication.run(SpringBootSentinelApplication.class, args);
    }

    public static void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource("queryOrderInfo3");
        // QPS限流
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // QPS控制在2以内
        rule1.setCount(2);
        rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        rule1.setLimitApp("default");
        rule1.setStrategy(RuleConstant.STRATEGY_DIRECT);
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
    }
//    public static void initFlowQpsRule(){
//        List<FlowRule> rules = new ArrayList<>();
//        FlowRule rule1 = new FlowRule();
//        rule1.setResource("zyping");
//        /*QPS 限制  设置限流规则*/
//        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
//        /*QPS 控制在2s以内 设置阈值
//         * QPS=2:表示1秒钟之内可以接入2个请求*/
//        rule1.setCount(2);
//        /*设置控制策略*/
//        rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
//        /*针对具体的客户端进行限流*/
//        rule1.setLimitApp("default");
//        /*流控模型*/
//        rule1.setStrategy(RuleConstant.STRATEGY_DIRECT);
//        rules.add(rule1);
//        /*核心代码: 加载规则*/
//        FlowRuleManager.loadRules(rules);
//    }

}
  • 演示结果:
    在这里插入图片描述
    1秒钟请求数超过2个则:
    在这里插入图片描述
3.2.2 流控规则设置2

集成SpringBoot 使用到了SPI机制
在这里插入图片描述
在这里插入图片描述

  • InitFunc 接口源码
package com.alibaba.csp.sentinel.init;

/**
 * @author Eric Zhao
 */
public interface InitFunc {

    void init() throws Exception;
}
  • 引入Jar包
<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>2.2.0.RELEASE</spring-boot.version>
    <spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-extension</artifactId>
    <version>1.8.0</version>
</dependency>
<!--<dependency>
    &lt;!&ndash;单机版本&ndash;&gt;
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.0</version>
</dependency>-->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>22.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • nacos配置
[
  {
      //对应下文中TestServer中doTest方法上的@SentinelSource的value值
     "resource":"doTest",
     //grade = 1以QPS进行限流
     //QPS = 5(1秒钟最多5次)
     "grade":1,
     "count":5,
    "clusterMode":false,
    "controlBehavior":0,
    "strategy":0
  },{
     "resource":"/hello/{name}",
     "grade":1,
     "count":5
  }
  ]

配置详情
在这里插入图片描述

  • FlowRuleInitFunc
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

import java.util.List;

public class FlowRuleInitFunc implements InitFunc {
    private final String nacosAddress = "196.168.65.224:8848";
    private final String groupId = "SENTINEL_GROUP";
    private final String dataId = "--flow-rules";

    private final String clusterServerHost = "localhost";
    private final int clusterServerPort = 9999;
    private final int requestTimeOut = 20000;
    private final String appName = "springboot-sentinel";
    
    /**
     * 参数初始化
     * @throws Exception
     */
    @Override
    public void init() throws Exception {
//        loadClusterConfig();
        registerFlowRule();
    }
    /**
     * 集群模式
     */
//    private void loadClusterConfig(){
//        ClusterClientAssignConfig assignConfig = new ClusterClientAssignConfig();
//        //放到配置中心
//        assignConfig.setServerHost(clusterServerHost);
//        assignConfig.setServerPort(clusterServerPort);
//
//        ClusterClientConfigManager.applyNewAssignConfig(assignConfig);
//
//        ClusterClientConfig clientConfig = new ClusterClientConfig();
//        /*放到配置中心*/
//        clientConfig.setRequestTimeout(requestTimeOut);
//        ClusterClientConfigManager.applyNewConfig(clientConfig);
//    }

    private void registerFlowRule(){
        /**
         * 动态配置文件
         */
        ReadableDataSource<String, List<FlowRule>> flowRuleDs =
                new NacosDataSource<List<FlowRule>>(nacosAddress, groupId, appName + dataId,
                                   source-> JSON.parseObject(source, new TypeReference<List<FlowRule>>(){}));
        /*将配置中心的配置注册到property中*/
         FlowRuleManager.register2Property(flowRuleDs.getProperty());
    }
}
  • SentienlController
@RestController
public class SentinelController {

    @Autowired
    TestService testService;

    @GetMapping("/hello/{name}")
    public String sayHello(@PathVariable("name") String name){
        return testService.doTest(name);
    }
}
  • TestService
@Service
public class TestService {

    @SentinelResource(value = "doTest",blockHandler ="blockHandler",fallback = "fallback") //声明限流的资源
    public String doTest(String name){
        return "hello , "+name;
    }
    public String blockHandler(String name, BlockException e){ //降级,限流触发的
       return "被限流了";
    }
    public String fallback(String name){ //熔断触发的
        return "被降级了";
    }

}

演示结果:
在这里插入图片描述
在这里插入图片描述

3.2.3 流控规则设置3

初始化文件在配置文件中 , 不用SPI

  • application.properties
# 应用名称
spring.application.name=springboot-sentinel

# 应用服务 WEB 访问端口
server.port=8080
# spring 静态资源扫描路径
spring.resources.static_locations=classpath:/static/

# Sentinel 控制台地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:7777
# 取消Sentinel控制台懒加载
# 默认情况下 Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包
# 配置 sentinel.eager=true 时,取消Sentinel控制台懒加载功能
spring.cloud.sentinel.eager=true

spring.cloud.sentinel.datasource.ds1.nacos.serverAddr=119.91.65.224:8848
spring.cloud.sentinel.datasource.ds1.nacos.dataId=springboot-sentinel--flow-rules
spring.cloud.sentinel.datasource.ds1.nacos.groupId=SENTINEL_GROUP
# FlowRule , json
spring.cloud.sentinel.datasource.ds1.nacos.dataType=json
spring.cloud.sentinel.datasource.ds1.nacos.ruleType=flow
spring.cloud.sentinel.datasource.ds1.nacos.username=nacos
spring.cloud.sentinel.datasource.ds1.nacos.password=nacos
  • 依赖
<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>2.2.0.RELEASE</spring-boot.version>
    <spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
        <version>1.8.0</version>
    </dependency>
    <!--<dependency>
        &lt;!&ndash;单机版本&ndash;&gt;
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-core</artifactId>
        <version>1.8.0</version>
    </dependency>-->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>22.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  • Nacos配置中心
[
  {
     "resource":"doTest2",
     "grade":1,
     "count":2,
    "clusterMode":false,
    "controlBehavior":0,
    "strategy":0
  },{
     "resource":"/hello/{name}",
     "grade":1,
     "count":5
  }
  ]

在这里插入图片描述

  • SentinelController
@RestController
public class SentinelController {

    @Autowired
    TestService testService;

    @GetMapping("/hello/{name}")
    public String sayHello(@PathVariable("name") String name){
        return testService.doTest(name);
    }
}
  • TestService
@Service
public class TestService {

    @SentinelResource(value = "doTest2",blockHandler ="blockHandler",fallback = "fallback") //声明限流的资源
    public String doTest(String name){
        return "hello , "+name;
    }
    /*public String blockHandler(String name, BlockException e){ //降级,限流触发的
       return "被限流了";
    }
    public String fallback(String name){ //熔断触发的
        return "被降级了";
    }*/
    public String blockHandler(String name, BlockException e){ //降级,限流触发的
        return "被限流了";
    }
    public String fallback(String name){ //熔断触发的
        return "被降级了";
    }
}

演示结果:
在这里插入图片描述
在这里插入图片描述

四、动态限流

  • poll方式
  • push方式
4.1 、集群限流

sentinel可以做集群限流,但是不建议使用sentinel做集群限流(引入token-server(需要单独部署 或者嵌入代码));

token-server官网都提供了代码;
在这里插入图片描述

  • 抽象解析:
    在这里插入图片描述
  • token-server代码实现

利用SPI 机制

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>1.7</maven.compiler.source>
  <maven.compiler.target>1.7</maven.compiler.target>
</properties>

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-cluster-server-default</artifactId>
    <version>1.8.0</version>
  </dependency>
  <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.0</version>
  </dependency>
  <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.0</version>
  </dependency>
</dependencies>
  • FlowRuleInitFunc
public class FlowRuleInitFunc implements InitFunc{
    private final String nacosAddress="192.168.8.139:8848";
    private final String groupId="SENTINEL_GROUP";
    private String dataId="-flow-rules";

    @Override
    public void init() throws Exception {
        ClusterFlowRuleManager.setPropertySupplier(namespace->{
            ReadableDataSource<String,List<FlowRule>> flowRuleDs=
                    new NacosDataSource<List<FlowRule>>(nacosAddress,groupId,namespace+dataId,
                            source-> JSON.parseObject(source,new TypeReference<List<FlowRule>>(){}));
            return flowRuleDs.getProperty();
        });
    }
}
  • ClusterServer
import com.alibaba.csp.sentinel.cluster.server.ClusterTokenServer;
import com.alibaba.csp.sentinel.cluster.server.SentinelDefaultTokenServer;
import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager;
import com.alibaba.csp.sentinel.cluster.server.config.ServerTransportConfig;
import java.util.Collections;
public class ClusterServer {

    public static void main(String[] args) throws Exception {
        ClusterTokenServer tokenServer=new SentinelDefaultTokenServer();
        //手动载入namespace和serverTransportConfig的配置到ClusterServerConfigManager
        //集群限流服务端通信相关配置
        ClusterServerConfigManager.loadGlobalTransportConfig(
                new ServerTransportConfig().setIdleSeconds(600).setPort(9999));
        //加载namespace集合列表() , namespace也可以放在配置中心
        ClusterServerConfigManager.loadServerNamespaceSet(Collections.singleton("App-Test"));
        tokenServer.start();
        //Token-client会上报自己的project.name到token-server。Token-server会根据namespace来统计连接数
    }
}

五、熔断、限流

熔断针对的是后端的请求,限流是针对前端的请求

/**
 * blockHandler :限流之后执行的方法;
 * fallback :熔断之后回调的方法
 */
@SentinelResource(value = "doTest3",blockHandler ="blockHandler",fallback = "fallback")
5.1 代码演示
  • GoodsQueryService

调用接口出现异常次数超过N次的时候, 进行熔断;

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.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

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

@Component
@Slf4j
public class GoodsQueryService {

    private static final String KEY = "queryGoodsInfo2";

    /**
     * 模拟商品查询接口
     *
     * @param spuId
     * @return
     */
    @SentinelResource(value = KEY, blockHandler = "blockHandlerMethod", fallback = "queryGoodsInfoFallback")
    public String queryGoodsInfo(String spuId) {

        // 模拟调用服务出现异常
        if ("0".equals(spuId)) {
            throw new RuntimeException();
        }

        return "query goodsinfo success, " + spuId;
    }

    public String blockHandlerMethod(String spuId, BlockException e) {
        log.warn("queryGoodsInfo222 blockHandler", e.toString());
        return "queryGoodsInfo error, blockHandlerMethod res: " + spuId;

    }

    public String queryGoodsInfoFallback(String spuId, Throwable e) {
        log.warn("queryGoodsInfo222 fallback", e.toString());
        return "queryGoodsInfo error, return fallback res: " + spuId;
    }

    /**
     * GoodQueryService执行方法前需要执行的方法
     */
    @PostConstruct
    public void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        rule.setResource(KEY);
        // 80s内调用接口出现异常次数超过5的时候, 进行熔断
        rule.setCount(5);
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        rule.setTimeWindow(80);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }
}

熔断:DegradeRuleManager
限流:FlowRuleManager

六、总结

6.1 限流规则有哪几个?
  1. QPS数进行限流 grade = 1 ;count=5
  2. 线程数进行限流, grade =0;
  3. 可以根据调用关系 flowRule设置关联性的资源
    rule.setRefResource(“xxx”)
6.2 控制策略
rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
  1. 直接拒绝(默认的)(CONTROL_BEHAVIOR_DEFAULT = 0;)
  2. 慢启动:(CONTROL_BEHAVIOR_WARM_UP = 1)当限流规则设置的QPS数量是10000,但是平常处理的请求数都是很小, 需要逐步增加;
  3. 匀速器模式(匀速排队CONTROL_BEHAVIOR_RATE_LIMITER = 2)超出阈值不拒绝,而是等待
6.3 限流模式
rule1.setStrategy(”“);
  1. 直连(STRATEGY_DIRECT = 0)(rule1.setResource= “queryOrderInfos”)
    只有调用这个资源时才会进行限流,即只有进入到
    @SentinelResource(value=“queryOrderInfos”)
    public String query(){} 方法时才进行限流
  2. 关联(STRATEGY_RELATE = 1)
    当关联的资源达到限流条件时,开启限流,适合做应用让步。例如为了一个查询的接口添加关联流控,关联资源为一个更新的接口,当更新的接口达到阈值时,开启查询接口的限流,为更好接口让步服务器资源
  3. 链路(STRATEGY_CHAIN = 2)
    当从某个接口过来的资源达到限流条件时,开启限流

sentinel主要是用来做限流,熔断用的不多

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值