Dubbo生态之sentinel限流

1. 限流算法

我们知道,在分布式架构中,当服务请求量过大时,容易对服务器造成不可预知的压力,因此,我们在客户端请求的时候,进行限流,起到一个保护的作用

常见的限流算法有: 计数器限流,滑动窗口限流、漏桶限流、令牌桶限流算法

1.1 计数器限流

计数器算法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么我们可以这么做:在一开 始的时候,我们可以设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个请求的间隔时间还在1分钟之内,那么说明请求数过多;如果该请求与第 一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重 置 counter

具体算法的示意图如下:

 

 假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,这种情况,其实是符合我们上述规则的。因为在0:00-0:59这个区间用户确实没有超过我们设置的100这个最大范围

临界值问题,如下图:

 因此,滑动窗口限流就对这个问题进行了改进

1.2 滑动窗口限流

发送和接受方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的窗口大小由接受方确定,目的在于控制发送速度,以免接受方的缓存不够大,而导致溢出,同时控制流量也可以避免网络拥塞。下面图中的4,5,6号数据帧已经被发送出去,但是未收到关联的ACK,7,8,9帧则是等待发送。可以看出发送端的窗口大小为6,这是由接受端告知的。此时如果发送端收到4号ACK,则窗口的左边缘向右收缩,窗口的右边缘则向右扩展,此时窗口就向
前“滑动了”,即数据帧10也可以被发送

 

 滑动窗口演示地址:Selective Repeat Protocol (pearsoncmg.com)

1.3 漏桶限流

漏桶算法思路是,不断的往桶里面注水,无论注水的速度是大还是小,水都是按固定的速率往外漏水;如果桶满了,水会溢出;

 桶本身具有一个恒定的速率往下漏水,而上方时快时慢的会有水进入桶内。

当桶还未满时,上方的水可以加入。一旦水满,上方的水就无法加入。桶满
正是算法中的一个关键的触发条件(即流量异常判断成立的条件)。

 在桶满水之后,常见的有两种处理方式为:

  • 暂时拦截住上方水的向下流动,等待桶中的一部分水漏走后,再放行上方水。
  • 溢出的上方水直接抛弃

特点:

  • 漏水的速率是固定的
  • 即使存在注水burst(突然注水量变大)的情况,漏水的速度也是固定的

1.4 令牌桶限流

令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。
令牌桶是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌; 令牌桶算法实际上由三部分组成:两个流和一个桶,分别是令牌流、数据流和令牌桶
  • 令牌流与令牌桶 
系统会以一定的速度生成令牌,并将其放置到令牌桶中,可以将令牌桶想象成一个缓冲区(可以用队列这种数据结构来实现),当缓冲区填满的时候,新生成的令牌会被扔掉。这里有两个变量很重要:
第一个是生成令牌的速度,一般称为 rate 。比如,我们设定 rate = 2 ,即每秒钟生成 2 个令牌,也就是每 1/2 秒生成一个令牌;
第二个是令牌桶的大小,一般称为 burst 。比如,我们设定 burst = 10 ,即令牌桶最大只能容纳 10 个令牌。
  • 数据流
数据流是真正的进入系统的流量,对于http接口来说,如果平均每秒钟会调用2次,则认为速率为 2次/s。

有以下三种情形可能发生

 数据流的速率等于令牌流的速率。这种情况下,每个到来的数据包或者请求都能对应一个令牌,然后无延迟地通过队列;

数据流的速率小于令牌流的速率 。通过队列的数据包或者请求只消耗了一部分令牌,剩下的令牌会在令牌桶里积累下来,直到桶被装满。剩下的令牌可以在突发请求的时候消耗掉。
数据流的速率大于令牌流的速率 。这意味着桶里的令牌很快就会被耗尽。导致服务中断一段时间,如果数据包或者请求持续到来,将发生丢包或者拒绝响应。

 比如前面举得例子,生产令牌得速率和令牌桶得大小分别为rate = 2,burst =10 ,则系统能承受得突发请求速率为10次/s,平均请求速率为2次/s.三种情形中得最后一种情形是这个算法得核心所在,这个算法非常精确,实现非常简单并且对服务器得压力可以忽略不计,因此应用的相当广泛。

 特点

  • 令牌可以积累:桶中最大的令牌数是b,表示可以积累的最大令牌数
  • 允许突发流量:桶中token可以积累到n(0=<n<=b),此时如果有n个突发请求同时到达,这n个请求时可以同时允许处理的。

2.Sentinel实现限流

sentinel是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。

选择sentinel作为限流组件,有很多理由,其中最有说服力的理由应该是

1. Sentinel承接了多次双十一大促流量的核心场景、包括秒杀、集群流量控制、实时熔断下游不可用服务
2. Sentinel提供了实时监控,可以通过控制台看到接入的应用的单台机器的秒级别数据
3. Sentinel 的生态支持,可以很简单的接入spring cloud、Dubbo、gRPC
4. 提供了SPI的扩展,可以通过SPI来扩展定制自己的限流规则

 

在Sentinel中,要实现流量控制,有两个核心的因素

  •  资源
简单来说,资源是指要被Sent   inel保护自来的目标对象,它是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
  •  规则
针对目标资源所设定的流控规则,它是围绕资源的实时状态设定的,可以包括流量控制规则、熔断降级规则以及系统保护规则,所有规则可以动态实时调整。
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。
在实际应用中,请求数量往往是随机的且不可控的,而系统的处理能力又有限,所以我们需要根据系统的处理能力对流量进行控制,而Sentinel的核心作用,就是针对目标资源使用对应的规则把随机的请求调整成合适的形状

 

流量控制通常有以下纬度:

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

Sentinel的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

3.限流应用实战

3.1 基于Semaphore实现一个线程流量的控制

public class SemaphoreExample {

    public static void main(String[] args) {
        ExecutorService executorService= Executors.newCachedThreadPool();
        Semaphore semaphore=new Semaphore(5); //5个令牌
        for (int i = 0; i < 20; i++) {
            final int NO=i;
            Runnable runnable=new Runnable() {
                @Override
                public void run() { //表示线程的执行逻辑
                    try {
                        semaphore.acquire(); //在执行之前,先获取一个令牌
                        System.out.println(Thread.currentThread().getName()+":执行业务逻辑:"+NO);
                        Thread.sleep(new Random().nextInt(1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        System.out.println(Thread.currentThread().getName()+":释放令牌");
                        semaphore.release(); //释放令牌
                    }
                }
            };
            executorService.execute(runnable);
        }
        executorService.shutdown();
    }

}

  

分析:  semaphore创建了5个令牌 可以看到有5个线程拿到了令牌,当有一个线程释放令牌后,下一个线程才能够进来继续执行

3.2 基于guava实现的线程限流

public class GuavaExample {

    RateLimiter rateLimiter=RateLimiter.create(10); //qps=10 每秒生成10个

    public void doRequest(){
        if(rateLimiter.tryAcquire()){ //获取一个令牌
            System.out.println(Thread.currentThread().getName()+":正常处理");
        }else{
            System.out.println(Thread.currentThread().getName()+":请求数量过多");
        }
    }

    public static void main(String[] args) throws IOException {
        GuavaExample ge=new GuavaExample();
        Random random=new Random();
        CountDownLatch latch=new CountDownLatch(1);
        for (int i=0;i<20;i++){
            new Thread(()->{
                try {
                    latch.await(); //让线程先阻塞
                    Thread.sleep(random.nextInt(1000));
                    ge.doRequest();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        latch.countDown();
        System.in.read();
    }
}

3.3 基于Sentinel的限流

 限流治理

a.引入jar包

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.5</version>
</dependency>

b. 代码演示:

public class SentinelExample {
    //构造块
    {
        initFlowRules();
    }

    //静态块
    static {

    }

    //流控控制保护对象  针对doRequest进行限流
    private final static String RESROUCE="doRequest";

    public static void main(String[] args) {
        SentinelExample se=new SentinelExample();
        se.doRequest();
    }

    public void doRequest(){
        while(true){
            try(Entry entry= SphU.entry(RESROUCE)){//java7,自动释放资源
                System.out.println(Thread.currentThread().getName()+":+执行业务逻辑");
            }catch (BlockException e){
                //如果被限流,就会抛出BlockedException
                e.printStackTrace();
            }
        }
    }

    private void initFlowRules(){
        FlowRule rule=new FlowRule(); //限流的规则
        rule.setCount(5);   // 每秒访问5个请求
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS); //针对QPS限流
        rule.setResource(RESROUCE); //被保护的资源
        List<FlowRule> rules=new ArrayList<>();
        rules.add(rule);
        FlowRuleManager.loadRules(rules); //让Sentinel加载限流规则
    }

}

分析: sentinel的使用主要围绕连个方向,定义什么样的规则,要限流或者说要保护的对象,基于这两点,initFlowRules方法就是定义规则和要保护的对象并且交由sentinel加载。采用while(true)不断的去请求资源。来模拟限流。

4.Springboot集成sentinel

a.引入jar包

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>1.8.5</version>
</dependency>

b.集成spring注入sentinel Bean 这是因为sentinel中不像nacos和dubbo中有着spring-context nacos-spring-context这样的包,sentinel中没有sentinel-spring-context这样的包,但是都是基于AOP实现的,因此可以采用Bean注入的方式注入,让@SentinelResource注解生效

@Configuration
public class SentinelConfigration {

    @Bean
    public SentinelResourceAspect sentinelResourceAspect(){
         return new SentinelResourceAspect();
    }
}

 c.sentinel规则的初始化

public class FlowRuleInitFunc implements InitFunc {

    @Override
    public void init() throws Exception {
        List<FlowRule> rules=new ArrayList<>();
        FlowRule rule1=new FlowRule(); //限流的规则
        rule1.setCount(2);
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS); //针对QPS限流
        rule1.setResource(SentinelMethodConstant.SAYMETHOD_NAME); //被保护的资源

        FlowRule rule2=new FlowRule(); //限流的规则
        rule2.setCount(2);
        rule2.setGrade(RuleConstant.FLOW_GRADE_QPS); //针对QPS限流
        rule2.setResource(SentinelMethodConstant.TEST_FALLBACK); //被保护的资源
        rules.add(rule1);
        rules.add(rule2);
        FlowRuleManager.loadRules(rules); //让Sentinel加载限流规则
    }
}

sentinel中提供了初始化的SPI,只要实现InitFunc,在静态资源里面配置好,则启动项目的时候就会自动加载该规则

com.zsc.sentinel.example.springbootsentinelexample.config.FlowRuleInitFunc

 d.对接口方法级别的限流使用

@Service
public class HelloService {
    Logger logger= LoggerFactory.getLogger(HelloService.class);

    // value 必填 针对哪个资源做流量控制
    // blockHandler 表示的是出现block exception时的处理方法
    // blockHandlerClass 表示的是出现block exception时的处理类
    @SentinelResource(value = SentinelMethodConstant.SAYMETHOD_NAME,
            blockHandlerClass = SayMethodException.class,
            blockHandler = "handleException"
    )
    public void say(){
        System.out.println("Hello, Sentinel");
    }


    // fallback 降级
    @SentinelResource(value = SentinelMethodConstant.TEST_FALLBACK,
    fallback = "helloFallback")
    public String testFallback(){
        return "Hello, Fallback Test";
    }

    public String helloFallback(){
        logger.info("触发了Fallback方法");
        return "我是Fallback的返回结果";
    }
    

}

 常量类与异常处理类

public class SentinelMethodConstant {

    public final static String SAYMETHOD_NAME="say";

    public final static String TEST_FALLBACK="testFallback";
}
public class SayMethodException {

    public static void handleException(BlockException e){
        e.printStackTrace();
        System.out.println("Occur Exception: "+e.getClass().getCanonicalName());
    }
}

4.1 sentinel-dashboard的使用

java -Dserver.port=9999 -Dcsp.sentinel.dashboard.server=localhost:9999 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.7.jar

sentinel的可视化看板

https://github.com/alibaba/Sentinel/在release版本中找到jar包

在cmd中执行命令

java -Dserver.port=9999 -Dcsp.sentinel.dashboard.server=localhost:9999 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.7.jar

 在浏览器直接打开localhost:9999  输入账号密码 sentinel可进入

5. dubbo集成sentinel

a.引入jar包

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-apache-dubbo-adapter</artifactId>
    <version>1.8.5</version>
</dependency>

b.写规则,与springboot集成一致,只是资源换成接口级别的了

public class DubboFuleRuleInit implements InitFunc {
    @Override
    public void init() throws Exception {
        List<FlowRule> rules=new ArrayList<>();
        FlowRule rule1=new FlowRule(); //限流的规则
        rule1.setCount(2);
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS); //针对QPS限流
        rule1.setResource("org.zsc.normal.user.ISentinelService"); //被保护的资源
        rules.add(rule1);
        FlowRuleManager.loadRules(rules); //让Sentinel加载限流规则
    }
}

c.接口正常实现

@DubboService(cluster = "failfast",timeout = 1000)
public class SentinelService implements ISentinelService {

    Logger logger= LoggerFactory.getLogger(SentinelService.class);

    @Override
    public String testSentinel() {
        logger.info("begin SentinelService.testSentinel Method");
        return "执行Sentinel测试方法,测试限流的功能";
    }
}

d.那么dubbo是怎么控制该接口限流的呢 在dubbo里面有个fillter会去对接口名进行限流

 6.总结

        sentinel除了本身功能的丰富性,还可以集成不同的组件实现流量的控制,同时还能够针对不同维度,不同的指标来控制,并且它还能通过dashboard进行整体流量的管理和监控,这样对我们的整个维护来说会更加友好。核心的两个功能是限流和熔断,本篇只是介绍了限流的使用,后续介绍熔断。限流核心的两个东西是规则和资源,就是说我针对这个资源要通过什么样的规则来实现这个资源的保护。所以,在sentinel中要强化的两个点就是flowrule和resource,resource可以是我们的应用、接口、方法等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/647824.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

猫头虎 解析:为什么AIGC在国内适合做TOB,在国外适合做TOC?

猫头虎 解析&#xff1a;为什么AIGC在国内适合做TOB&#xff0c;在国外适合做TOC&#xff1f; 博主 猫头虎 的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面…

Golang | Leetcode Golang题解之第114题二叉树展开为链表

题目&#xff1a; 题解&#xff1a; func flatten(root *TreeNode) {curr : rootfor curr ! nil {if curr.Left ! nil {next : curr.Leftpredecessor : nextfor predecessor.Right ! nil {predecessor predecessor.Right}predecessor.Right curr.Rightcurr.Left, curr.Righ…

python数据分析——apply 1

参考资料&#xff1a;活用pandas库 apply是指把函数同时作用于DataFrame的每一行或每一列。类似于编写一些跨每行或每列的for循环&#xff0c;并同时调用apply函数。 1、函数 函数是对python代码进行分组和复用的一种方法。如果某段代码会被多次使用&#xff0c;并且使用时是需…

【C++】——入门基础知识超详解

目录 ​编辑 1.C关键字 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 命名空间的使用有三种方式&#xff1a; 注意事项 3. C输入&输出 示例 1&#xff1a;基本输入输出 示例 2&#xff1a;读取多个值 示例 3&#xff1a;处理字符串输入 示例 4&#xff1a;读…

部署PIM-SM

拓扑图 配置 使能组播路由 配置OSPF 组播路由器接口配置pim-sm 连接组成员的接口使能igmp pim路由器上配置静态RP sysname AR1 # multicast routing-enable # interface GigabitEthernet0/0/0ip address 10.1.12.1 255.255.255.0 pim sm # interface GigabitEthernet0/0/…

SpringBoot + MybatisPlus

SpringBoot MybatisPlus 整合记录 1. 硬件软件基本信息2. 相关链接3. 通过idea快速生成一个Springboot项目4. 启动报错问题解决问题一&#xff1a;Springboot启动的时候报错提示 “没有符合条件的Bean关于Mapper类型”问题二&#xff1a;启动的时候提示需要一个Bean&#xff0…

文件操作知识点

前言: 我们应该知道一般程序运行时产生的数据是存放在内存中的。但是如果程序关闭后这些内存就会被系统回收&#xff0c;如果内存内的有用的数据没有被保存下来&#xff0c;这些数据就丢失了。所以这个时候我们就可以使用磁盘来储存我们的数据。 目录 程序文件的分类 文件名…

第八篇【传奇开心果系列】Python微项目技术点案例示例:以微项目开发为案例,深度解读Dearpygui 编写图形化界面桌面程序的优势

传奇开心果博文系列 系列博文目录Python微项目技术点案例示例系列 博文目录前言一、开发图形化界面桌面程序的优势介绍二、跨平台特性示例代码和解析三、高性能特性示例代码和解析四、简单易用特性示例代码和解析五、扩展性强示例代码和解析六、现代化设计示例代码和解析七、知…

Android 快速调试网络 复制curl 到postMan

搜索这个插件 官网地址&#xff1a;https://github.com/itkacher/OkHttpProfiler 集成教程也在里面集成完毕后右下角有一个入口点进去可以复制curl| 插件名称&#xff1a;Okhttp Profiler 真的很好用&#xff01;

软测刷题-错题1

提高测试效率的方法&#xff1a; 1、不要做无效的测试 2.不要做重复的测试 3.不同测试版本的测试侧重点 4.优化测试顺序 LoadRunner是对服务器进行施压。 在数据库中存在的用户数是指注册用户数。 input标签可以直接使用send_keys实现上传&#xff0c;而非input标签是无法直…

指纹识别系统架构

目录 1. 系统架构 1.1 指纹采集模块 1.2 指纹处理模块 1.3 指纹登记模块 1.4 指纹识别模块 1.5 指纹识别决策模块 1.6 管理模块 1.6.1 存储管理 1.6.2 传输管理 1.6.3 安全管理 1.7 应用开放功能 1.7.1 指纹登记功能 1.7.2 指纹验证功能 1.7.3 指纹辨识功能 2. …

SparkStreaming架构原理(详解)

Spark概述 SparkStreaming架构原理 Spark Streaming的架构主要由以下几个关键部分组成。 1.数据源接收器&#xff08;Receiver&#xff09; 执行流程开始于数据源接收阶段&#xff0c;其中接收器&#xff08;Receiver&#xff09;负责从外部数据源获取数据流。 接收器可以连…

Android Studio开发之路(十四)自定义Titlebar以及设置顶部状态栏颜色

一、描述 项目需求&#xff0c;我要做一个下图这样的titlebar,包括一个返回按钮&#xff0c;一个关闭按钮&#xff0c;一个文本框。默认的titlebar按钮设计不太满足我的需求&#xff0c;于是我打算自定义一个titlebar组件&#xff0c;应用到我的每一个页面 二、titlebar组件设…

flink程序本地运行:No ExecutorFactory found to execute the application

1.问题描述 在idea中运行flink job程序出现如下错误&#xff1a; Exception in thread "main" java.lang.IllegalStateException: No ExecutorFactory found to execute the application. at org.apache.flink.core.execution.DefaultExecutorServiceLoader.getE…

【蓝桥杯】国赛普及-

题目列表 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) P9420 [蓝桥杯 2023 国 B] 子 2023 / 双子数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<bits/stdc.h> using llunsigned long long; #define int ll const int N2e510; int k0; std::string s; int…

vs2019 c++20 规范的 STL 库的智能指针 shared、unique 、weak 及 make_** 函数的源码注释汇总,和几个结论

智能指针的源码都在 《memory》 头文件中。因为头文件太长&#xff0c;再者本次整理是基于以前的零散的模板分析。故相当于抽取了该头文件中关于智能指针的源码进行分析&#xff0c;注释。 &#xff08;1 探讨一&#xff09;当独占指针指向数组时&#xff0c;其默认的删除器是…

HTML橙色爱心

目录 写在前面 准备开始 完整代码 运行结果 系列文章 写在后面 写在前面 本期小编给大家分享一颗热烈且浪漫的爱心&#xff0c;快来看看吧&#xff01; 准备开始 在开始之前&#xff0c;我们需要先简单的了解一下这颗爱心的原理哦~ 本期将用html实现这颗跳动的爱心&a…

0基础认识C语言

为了给0基础一个舒服的学习路径&#xff0c;就有了这个专栏希望带大家一起进步。 话不多说&#xff0c;开始正题。 一、C语言的一段小历史 C语言的设计要追溯到20世纪60年代末和70年代初&#xff0c;在那个时代美国有这么一号人叫做丹尼斯.里奇&#xff0c;他和同事肯.汤普逊…

redis数据操作相关命令

1.list操作 1.1 rpush rpush&#xff1a;新的元素添加到list最右边 #从右边依次往List添加1,2,3 RPUSH name 1 RPUSH name 2 RPUSH name 3#查看列表&#xff1a;返回 1,2,3 LRANGE name 0 -1结果如下&#xff1a; 1.2 lpush lpush&#xff1a;新加的元素在list最左边 #从…

WordPress安装插件失败No working transports found

1. 背景&#xff08;Situation&#xff09; WordPress 社区有非常多的主题和插件&#xff0c;大部分人用 WordPress 都是为了这些免费好用的主题和插件。但是今天安装完 WordPress 后安装插件时出现了错误提示&#xff1a;“ 安装失败&#xff1a;下载失败。 No working trans…