项目中如何进行限流(限流的算法、实现方法详解)
❤ 作者主页:李奕赫揍小邰的博客
❀ 个人介绍:大家好,我是李奕赫!( ̄▽ ̄)~*
🍊 记得点赞、收藏、评论⭐️⭐️⭐️
📣 认真学习!!!🎉🎉
文章目录
- 限流的算法
- 漏桶算法
- 令牌桶算法
- 限流粒度
- 限流实现构思
- 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--; //令牌数量-1return 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) {// 创建RedissonClientRedissonClient 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;@Beanpublic 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 {@Resourceprivate 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());
相关文章:

项目中如何进行限流(限流的算法、实现方法详解)
❤ 作者主页:李奕赫揍小邰的博客 ❀ 个人介绍:大家好,我是李奕赫!( ̄▽ ̄)~* 🍊 记得点赞、收藏、评论⭐️⭐️⭐️ 📣 认真学习!!!🎉🎉 文章目录 限流的算法漏…...
https在win7的环境下如何配置
https在win7的环境下如何配置?在Windows7环境下配置https,需要完成以下步骤: 1)安装Web服务器软件 可以选择安装常用的Web服务器软件,如Apache、Nginx或IIS,这些服务器软件都支持https。 2)获…...

Day69:WEB攻防-Java安全JWT攻防Swagger自动化算法签名密匙Druid泄漏
目录 Java安全-Druid监控-未授权访问&信息泄漏 黑盒发现 白盒发现 攻击点 Java安全-Swagger接口-导入&联动批量测试 黑盒发现 白盒发现 自动化发包测试 自动化漏洞测试 Java安全-JWT令牌-空算法&未签名&密匙提取 识别 JWT 方式一:人工识…...

Python Windows系统 虚拟环境使用
目录 1、安装 2、激活 3、停止 1、安装 1)为项目新建一个目录(比如:目录命名为learning_log) 2)在终端中切换到这个目录 3)执行命令:python -m venv ll_env,即可创建一个名为ll…...

栈和队列的学习
存储方式分两类:顺序存储和链式存储 栈:只允许从一端进行数据插入和删除的线性表:先进后出 FILO 队列:只允许从一端进行数据插入,另一端进行数据删除的线性表:先进先出 FIFO 栈 创建空栈,创建…...

【机器学习】基于机器学习的分类算法对比实验
摘要 基于机器学习的分类算法对比实验 本论文旨在对常见的分类算法进行综合比较和评估,并探索它们在机器学习分类领域的应用。实验结果显示,随机森林模型在CIFAR-10数据集上的精确度为0.4654,CatBoost模型为0.4916,XGBoost模型为…...
民航电子数据库:mysql与cae建表语法差异
目录 一、场景二、语法差异 一、场景 1、使用CAEMigrator-1.0.exe将mysql数据库迁移至cae数据库时,迁移速度非常慢,而且容易卡死(可能是部署cae数据库的服务器资源不足导致) 2、所以将mysql数据库导出为sql脚本,通过…...

(学习日记)2024.03.15:UCOSIII第十七节:任务的挂起和恢复
写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈。 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录&a…...

聚类分析 | Matlab实现基于NNMF+DBO+K-Medoids的数据聚类可视化
聚类分析 | Matlab实现基于NNMFDBOK-Medoids的数据聚类可视化 目录 聚类分析 | Matlab实现基于NNMFDBOK-Medoids的数据聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 NNMFDBOK-Medoids聚类,蜣螂优化算法DBO优化K-Medoids 非负矩阵分解(…...

Unity类银河恶魔城学习记录11-3 p105 Inventory UI源代码
Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI_itemSlot.cs using System.Collections; using System.Collections.Gen…...
Vue3 + Vite + ts引入本地图片
Vue3 Vite ts引入本地图片 单张图片导入 单个图片导入,不过多阐述,通过 import 导入需要使用的图片。 import imgName from /assets/img/imgName.png 多张图片导入 new URL() import.meta.url import.meta.url 是一个 ESM 的原生功能࿰…...

图斑或者道路如何单独显示名称在图斑上或者道路上
0序: 遇到过多个测绘、工程、林业相关业务的客户,在加载一些图斑数据,线路数据时,希望能够单独的把图斑的名称,显示到图斑上,或者路网上面。 之前多数推荐的办法: 1.shp可以直接在图新地球中…...
docker 修改默认存储位置
一般系统下系统盘可能磁盘空间有限,需要将docker的存储目录改到其他位置 docker info 查看docker的版本 低版本docker在配置json中增加"graph":"/var/lib/docker" 高版本docker在配置json中增加"data-root":"/var/lib/docker&q…...

Springboot+vue的医疗挂号管理系统+数据库+报告+免费远程调试
效果介绍: Springbootvue的医疗挂号管理系统,Javaee项目,springboot vue前后端分离项目 本文设计了一个基于Springbootvue的前后端分离的医疗挂号管理系统,采用M(model)V(view)C(con…...
【Effective C++】39 明智而审慎地使用private继承
在之前论证过c如何将public 继承视为 is-a 关系。在哪个例子里,class Student 以 public 形式继承class Person, 于是编译器在必要时刻将Students暗自转化为Person.如果此时我们以 private 继承替换 public继承。 class Person {...}; class Student: p…...

2024年卫生巾行业市场分析报告(京东天猫淘宝线上卫生巾品类电商数据查询)
最近,相关部门辟谣了一则“十大致癌卫生巾黑名单”的消息。这个榜单是部分博主AI撰写,为博眼球、蹭热度的结果。此次事件势必会对卫生巾行业产生一定影响,加剧行业竞争。 根据鲸参谋电商数据平台显示,2024年1月至2月线上电商平台…...

MySQL之表的记录操作
前言 存数据不是目的,目的是能够将存起来的数据取出来或者查出来,并且能够对数据进行增删改查操作,本文将详细介绍表中记录的增删改查操作。对记录的操作属于DML数据库操作语言,可以通过SQL实现对数据的操作,包括实现…...

一种动态联动的实现方法
安防领域中的联动规则 有安防领域相关的开发经历的人知道,IPCamera可以配置使能“侦测”功能,并且指定仅针对图像传感器的某个区载进行侦测。除了基本的“移动侦测"外,侦测的功能点还有细化的类别,如人员侦测、车辆侦测、烟…...

kotlin中使用ViewBinding绑定控件
kotlin中使用ViewBinding绑定控件 什么是ViewBinding? View Binding是Android Studio 3.6推出的新特性,主要用于减少findViewById的冗余代码,但内部实现还是通过使用findViewById。通过ViewBinding,可以更轻松地编写可与视图交互…...

知识积累(五):Transformer 家族的学习笔记
文章目录 1. RNN1.1 缺点 2. Transformer2.1 组成2.2 Encoder2.2.1 Input Embedding(嵌入层)2.2.2 位置编码2.2.3 多头注意力2.2.4 Add & Norm 2.3 Decoder2.3.1 概览2.3.2 Masked multi-head attention 2.4 Transformer 模型的训练和推理2.4.1 训练…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...