项目中如何进行限流(限流的算法、实现方法详解)

news2024/11/15 21:20:32

❤ 作者主页:李奕赫揍小邰的博客
❀ 个人介绍:大家好,我是李奕赫!( ̄▽ ̄)~*
🍊 记得点赞、收藏、评论⭐️⭐️⭐️
📣 认真学习!!!🎉🎉

文章目录

  • 限流的算法
    • 漏桶算法
    • 令牌桶算法
  • 限流粒度
  • 限流实现构思
    • 1.本地限流(单机限流)
    • 2.分布式限流(多机限流)
  • Redisson 限流实现
    • 1.先引入依赖
    • 2.创建 RedissonConfig 配置类,用于初始化 RedissonClient 对象单例;
    • 3.编写 RedisLimiterManager
    • 4.在项目引用

  最近在写一个项目,需要控制用户使用系统的次数,以避免超支,比如给不同等级的用户分配不同的调用次数,防止用户过度使用系统造成破产。现在要做一个解决方案,就是限流,比如说限制单个用户在每秒只能使用一次,那这里我们怎么去思考这个限流的阈值是多少?多少合适呢?
  参考正常用户的使用,比如限制单个用户在每秒只能使用 1 次。
 

限流的算法

  限流的算法有很多,常用的有两个漏桶算法和令牌桶算法,接下来会讲一下这两个算法的用法。

漏桶算法

  它的原理很简单,可以认为就是注水漏水的过程。往漏桶中以任意速率流入水,以固定的速率流出水。当水超过桶的容量时,会被溢出,也就是被丢弃。因为桶容量是不变的,保证了整体的速率。
在这里插入图片描述
1.流入的水滴,可以看作是访问系统的请求,这个流入速率是不确定的。
2.桶的容量一般表示系统所能处理的请求数。
3.如果桶的容量满了,就达到限流的阀值,就会丢弃水滴(拒绝请求)
4.流出的水滴,是恒定过滤的,对应服务按照固定的速率处理请求。
伪代码如下:

 /**
     * 每秒处理数(出水率)
     */
    private long rate;

    /**
     *  当前剩余水量
     */
    private long currentWater;

    /**
     * 最后刷新时间
     */
    private long refreshTime;

    /**
     * 桶容量
     */
    private long capacity;

    /**
     * 漏桶算法
     * @return
     */
    boolean leakybucketLimitTryAcquire() {
        long currentTime = System.currentTimeMillis();  //获取系统当前时间
        long outWater = (currentTime - refreshTime) / 1000 * rate; //流出的水量 =(当前时间-上次刷新时间)* 出水率
        long currentWater = Math.max(0, currentWater - outWater); // 当前水量 = 之前的桶内水量-流出的水量
        refreshTime = currentTime; // 刷新时间

        // 当前剩余水量还是小于桶的容量,则请求放行
        if (currentWater < capacity) {
            currentWater++;
            return true;
        }
        
        // 当前剩余水量大于等于桶的容量,限流
        return false;
    }

没有办法迅速处理一批请求,面对突发流量的时候,漏桶算法还是循规蹈矩地处理请求,按照固定速率处理请求,这不是我们想要的结果。
 

令牌桶算法

  面对突发流量的时候,我们可以使用令牌桶算法限流。

令牌桶算法原理:
1.有一个令牌管理员,根据限流大小,定速往令牌桶里放令牌。
2.如果令牌数量满了,超过令牌桶容量的限制,那就丢弃。
3.系统在接受到一个用户请求时,都会先去令牌桶要一个令牌。如果拿到令牌,那么就处理这个请求的业务逻辑;
4.如果拿不到令牌,就直接拒绝这个请求。
在这里插入图片描述
伪代码如下:

   /**
     * 每秒处理数(放入令牌数量)
     */
    private long putTokenRate;
    
    /**
     * 最后刷新时间
     */
    private long refreshTime;

    /**
     * 令牌桶容量
     */
    private long capacity;
    
    /**
     * 当前桶内令牌数
     */
    private long currentToken = 0L;

    /**
     * 漏桶算法
     * @return
     */
    boolean tokenBucketTryAcquire() {

        long currentTime = System.currentTimeMillis();  //获取系统当前时间
        long generateToken = (currentTime - refreshTime) / 1000 * putTokenRate; //生成的令牌 =(当前时间-上次刷新时间)* 放入令牌的速率
        currentToken = Math.min(capacity, generateToken + currentToken); // 当前令牌数量 = 之前的桶内令牌数量+放入的令牌数量
        refreshTime = currentTime; // 刷新时间
        
        //桶里面还有令牌,请求正常处理
        if (currentToken > 0) {
            currentToken--; //令牌数量-1
            return true;
        }
        
        return false;
    }

如果令牌发放的策略正确,这个系统即不会被拖垮,也能提高机器的利用率。Guava的RateLimiter限流组件,就是基于令牌桶算法实现的
 

限流粒度

1.针对某个方法限流,即单位时间内最多允许同时 XX 个操作使用这个方法
2.针对某个用户限流,比如单个用户单位时间内最多执行 XX 次操作
3.针对某个用户 x 方法限流,比如单个用户单位时间内最多执行 XX 次这个方法
 

限流实现构思

1.本地限流(单机限流)

  每个服务器单独限流,一般适用于单体项目,就是你的项目只有一个服务器 。
  在 Java 中,有很多第三方库可以用来实现单机限流:Guava RateLimiter:这是谷歌 Guava 库提供的限流工具,可以对单位时间内的请求数量进行限制。


import com.google.common.util.concurrent.RateLimiter;

public static void main(String[] args) {
    // 每秒限流5个请求
    RateLimiter limiter = RateLimiter.create(5.0);
    while (true) {
        if (limiter.tryAcquire()) {
            // 处理请求
        } else {
            // 超过流量限制,需要做何处理
        }
    }
}

 

2.分布式限流(多机限流)

  如果你的项目有多个服务器,比如微服务,那么建议使用分布式限流。
1.把用户的使用频率等数据放到一个集中的存储进行统计;
比如 Redis,这样无论用户的请求落到了哪台服务器,都以集中存储中的数据为准。
(Redisson – 是一个操作 Redis 的工具库)
2.在网关集中进行限流和统计(比如 Sentinel、Spring Cloud Gateway)

//redission
import org.redisson.Redisson;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;

public static void main(String[] args) {
    // 创建RedissonClient
    RedissonClient redisson = Redisson.create();

    // 获取限流器
    RSemaphore semaphore = redisson.getSemaphore("mySemaphore");

    // 尝试获取许可证
    boolean result = semaphore.tryAcquire();
    if (result) {
        // 处理请求
    } else {
        // 超过流量限制,需要做何处理
    }
}

 

Redisson 限流实现

  Redisson 内置了一个限流工具类,可以帮助你利用 Redis 来存储、来统计。

1.先引入依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.21.3</version>
</dependency>

2.创建 RedissonConfig 配置类,用于初始化 RedissonClient 对象单例;

application.yml文件
在这里插入图片描述
RedissonConfig 配置类

@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {

    private Integer database;

    private String host;

    private Integer port;

    private String password;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setDatabase(database)
                .setAddress("redis://" + host + ":" + port);
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }
}

3.编写 RedisLimiterManager

  Manager是专门提供 RedisLimiter 限流基础服务的(提供了通用的能力,可以放到任何一个项目里)。

@Service
public class RedisLimiterManager {

    @Resource
    private RedissonClient redissonClient;

    /**
     * 限流操作
     *
     * @param key 区分不同的限流器,比如不同的用户 id 应该分别统计
     */
    public void doRateLimit(String key) {
     
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        // 限流器的统计规则(每秒2个请求;连续的请求,最多只能有2个请求被允许通过)
        // RateType.OVERALL表示速率限制作用于整个令牌桶,即限制所有请求的速率
        rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);
        // 每当一个操作来了后,请求一个令牌
        boolean canOp = rateLimiter.tryAcquire(1);
        // 如果没有令牌,还想执行操作,就抛出异常
        if (!canOp) {
            throw new BusinessException(ErrorCode.TOO_MANY_REQUEST);
        }
    }
}

之后进行一下测试即可。

4.在项目引用

伪代码,只需要引入RedisLimiterManager 类,直接在需要限流的地方加上即可,例如给用户加上限流,就是每个用户每秒只允许请求两次

@Resource
private RedisLimiterManager redisLimiterManager;


User loginUser = userService.getLoginUser(request);
// 限流判断,每个用户一个限流器
redisLimiterManager.doRateLimit("user_" + loginUser.getId());

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jdk api之UnsupportedEncodingException基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

力扣HOT100 - 49. 字母异位词分组

解题思路&#xff1a; 排序 注意&#xff1a; 返回时不能用List&#xff0c;因为List是抽象类&#xff0c;return的必须是List的具体实现&#xff0c;如ArrayList class Solution {public List<List<String>> groupAnagrams(String[] strs) {Map<String, Lis…

哈希存储、哈希表、哈希表的基本操作、算法的一些概念

我要成为嵌入式高手之3月25日数据结构第七天&#xff01;&#xff01; ————————————————————————————— 搜索(查找)二叉树 思想&#xff1a;左大右小 主要为了进行二分查找&#xff0c;由于根节点选择不合适&#xff0c;容易造成树不平衡&#…

NOMA免调度接入技术

标题 系统模型 参考视频&#xff1a;添加链接描述 利用接收机的复杂度提升为代价&#xff0c;提升频谱效率。为了保证上行方向上面&#xff0c;能够接入更多的用户&#xff0c;NOMA的根本思路&#xff0c;就是让多个用户占用相同的资源进行上行传输 系统模型 采用TDMA的方式…

K8s-网络原理-下篇

引言 本文是《深入剖析 K8s》的学习笔记&#xff0c;相关图片和案例可从https://github.com/WeiXiao-Hyy/k8s_example中获取&#xff0c;欢迎Star! K8s 的网络隔离: NetWorkPolicy K8s 如何考虑容器之间网络的“隔离” -> NetWorkPolicy 以下是一个 NetWorkPolicy 的定义…

msvcp110.dll丢失修复办法

在计算机使用过程中&#xff0c;我们经常会遇到一些扩展名为.dll的文件&#xff0c;这些文件是动态链接库文件&#xff0c;用于提供程序运行时所需的函数和资源。其中&#xff0c;msvcp110.dll文件是一个非常重要的动态链接库文件&#xff0c;它属于Microsoft Visual C 2012 Re…

【vue核心技术实战精讲】1.3 - 1.5 VUE 指令 (上)

文章目录 前言 本节内容1、v-text 和 v-html代码效果 2、v-if 和 v-show代码效果 3、v-bind3.1、用法&#xff1a;v-bind: 属性 &#xff0c;简写 :3.2、动态 attribute 名效果 3.3、内联字符串拼接效果 3.4、绑定 class效果 3.5、style 绑定3.6、绑定一个全是 attribute 的对…

学点儿Java_Day10_集合框架(List、Set、HashMap)

1 简介 ArrayList: 有序(放进去顺序和拿出来顺序一致)&#xff0c;可重复 HashSet: 无序(放进去顺序和拿出来顺序不一定一致)&#xff0c;不可重复 Testpublic void test1() {String[] array new String[3];//List: 有序 可重复//有序: 放入顺序 与 拿出顺序一致&#xff0c;…

精酿啤酒:酿造工艺的传承与改进

啤酒酿造工艺是一种历史悠久且不断发展的技艺&#xff0c;它随着时代的变化和技术的进步不断得到改进和创新。Fendi Club啤酒作为一家精酿啤酒品牌&#xff0c;在传承经典酿造工艺的同时&#xff0c;也不断探索和改进&#xff0c;以满足现代消费者的需求。 Fendi Club啤酒传承了…

蓝桥杯物联网遇见的重大BUG及其产生原因和解决方法

BUG列表 1、ADC的RP2显示一直为0&#xff1a;2、LORX_Tx发送数据乱码&#xff1a;3、strcmp比较char a[2] {1, 2}与“12”字符串是否相等板子会死机&#xff1a;4、LORA_Tx和LORA_Rx放一起会接收不到数据&#xff1a;5、RTC获取到静止时间&#xff1a;6、ADC获取RP1和RP2模拟量…

【k8s网络】梳理cni发展脉络

参考 《深入剖析 Kubernetes&#xff08;张磊&#xff09;》 补充 详解 Calico 三种模式&#xff08;与 Fannel 网络对比学习&#xff09;_calico vxlan-CSDN博客 容器网络 容器的网络栈 每个容器有自己的 net namespace net namespace 可以称之为网络栈所谓“网络栈”&…

睿考网:不是会计专业能考中级会计师吗?

不是会计专业也是可以考中级会计师的&#xff0c;中级会计师报名条件中并没有对专业做明确的限制&#xff0c;不同的学历对工作年限的要求不一样&#xff0c;如果考生满足报考条件就可以参加。 1.具备大学专科学历&#xff0c;从事会计工作满5年。 2.具备大学本科学历或学士学…

【Linux】进程的基本概念(进程控制块,ps命令,top命令查看进程)

目录 01.进程的基本概念 程序与进程 进程的属性 02.进程控制块&#xff08;PCB&#xff09; task_struct的内容分类 组织进程 03.查看进程 ps命令 top指令 在计算机科学领域&#xff0c;进程是一项关键概念&#xff0c;它是程序执行的一个实例&#xff0c;是操作系统的…

【Linux】线程预备知识{远程拷贝/重入函数与volatile关键字/认识SIGCHILD信号/普通信号/实时信号}

文章目录 0.远程拷贝1.重入函数与volatile关键字2.认识SIGCHILD信号3.普通信号/实时信号 0.远程拷贝 打包资源&#xff1a;tar czf code.tgz *远程传输&#xff1a;scp code.tgz usr服务器ip:/home/usr/路径解压&#xff1a;tar xzf code.tgz 1.重入函数与volatile关键字 先看…

【智能算法】秃鹰搜索算法(BES)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2020年&#xff0c; Alsattar等人受到秃鹰猎食自然行为启发&#xff0c;提出了秃鹰搜索算法&#xff08;Bald Eagle Search&#xff0c;BES&#xff09;。 2.算法原理 2.1算法思想 BES主要分为三…

龙智亮相2024国际集成电路展览会暨研讨会(IIC Shanghai),分享芯片研发及管理解决方案与技术实践

2024年3月28-29日&#xff08;周四-周五&#xff09;&#xff0c;上海张江科学会堂&#xff0c;2024国际集成电路展览会暨研讨会&#xff08;IIC Shanghai 2024&#xff09;即将盛大开幕。龙智携芯片研发及管理解决方案、最佳实践与案例&#xff0c;以及惊喜大奖在#1A14展位等着…

第十九章 linux部署scrapyd

文章目录 1. linux部署python环境1. 部署python源文件环境2. 下载python3. 解压安装包4. 安装5. 配置环境变量6. 检查是否安装成功7. 准备python使用的包8. 安装scrapyd9. 配置scrapyd10. 开放6800端口 2. 部署gerapy1. 本机下载包2. 初始化3. 进入gerapy同步数据库4. 创建用户…

堂哥让我给他做个真人动漫头像

背景 堂哥最喜欢的动漫是死神。他给了我一张死神主角一户的头像&#xff0c;以及自己的头像&#xff0c;希望我产出一张真人动漫头像。 一户的头像&#xff1a; 堂哥自拍照&#xff1a; 最近&#xff0c;有大佬部署了个stable diffusion&#xff0c;正好拿来一试身手。 stab…

优质视频素材库排行榜前十名有哪些?

在视频创作的世界中&#xff0c;每一帧画面、每一个音符都承载着无限的可能。为了帮助你更好地探索这些可能性&#xff0c;我继续为你精选了一系列素材网站。这些网站不仅能够提供高质量的视频、音效和图像素材&#xff0c;还能激发你的创意灵感&#xff0c;助你一臂之力。 1&…