【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter)

文章目录

  • 1. 什么是限流
  • 2. 常见的限流策略
    • 2.1 漏斗算法
    • 2.2 令牌桶算法
    • 2.3 次数统计
  • 3. 令牌桶代码编写
  • 4. 接口测试
  • 5. 测试结果

1. 什么是限流

限流就是在用户访问次数庞大时,对系统资源的一种保护手段。高峰期,用户可能对某个接口的访问频率急剧升高,后端接口通常需要进行DB操作,接口访问频率升高,DB的IO次数就显著增高,从而极大的影响整个系统的性能。如果不对用户访问频率进行限制,高频的访问容易打跨整个服务

2. 常见的限流策略

2.1 漏斗算法

我们想象一个漏斗,大口用于接收客户端的请求,小口用于流出用户的请求。漏斗能够保证流出请求数量的稳定。

在这里插入图片描述

2.2 令牌桶算法

令牌桶算法,每个请求想要通过,就必须从令牌桶中取出一个令牌。否则无法通过。而令牌会内部会维护每秒钟产生的令牌的数量,使得每秒钟能够通过的请求数量得到控制

在这里插入图片描述

2.3 次数统计

次数统计的方式非常直接,每一次请求都进行计数,并统计时间戳。如果下一次请求携带的时间戳在一定的频率内,进行次数的累加。如果次数达到一定阈值,则拒绝后续请求。直到下一次请求时间戳大于初始时间戳,重置接口次数与时间戳

在这里插入图片描述

3. 令牌桶代码编写

令牌桶算法我们可以使用Google guava包下的封装好的RateLimiter,紧紧抱住大爹大腿

另外,ip频率限制是一个横向逻辑,该功能应该保护所有后端接口,因此我们可以采用Spring AOP增强所有后端接口

另外,我们需要对同一个用户,对同一个接口访问次数进行限流,这意味着我们需要限制的是——(用户,接口)这样的一对元组。用户可以通过ip进行限定,也就是说,后端是同一个ip针对同一个请求的访问进行限流

因此我们需要为每一个这样的(ip,method)使用令牌桶限流,(ip,method)-> RateLimiter。ip + method这一对元组唯一确定一个RateLimiter

我们可以采用Map缓存这样的一一对应的关系

But,HashMap显然不适合,应为HashMap不防并发;另外ConcurrentHashMap也不合适,假如一个用户发出一个请求后就下线了,那么这个key就会长久的存活于内存中,这极大的增加了内存的压力

因此我们采用Google的Cache

Google大爹提供的Cache功能极其强大,读者可以自行阅读下面文档

/*** A builder of {@link LoadingCache} and {@link Cache} instances having any combination of the* following features:** <ul>*   <li>automatic loading of entries into the cache*   <li>least-recently-used eviction when a maximum size is exceeded*   <li>time-based expiration of entries, measured since last access or last write*   <li>keys automatically wrapped in {@code WeakReference}*   <li>values automatically wrapped in {@code WeakReference} or {@code SoftReference}*   <li>notification of evicted (or otherwise removed) entries*   <li>accumulation of cache access statistics* </ul>* /

IpLimiterAspect.java

import com.fgbg.demo.utils.RequestUtils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.TimeUnit;/*** 限制每个ip对同一个接口的访问频率*/
@Component
@Aspect
@Slf4j
@RestController
public class IpLimiterAspect {@Autowiredprivate RequestUtils requestUtils;// 每秒生成1个令牌, 同个ip访问同个接口的QPS为1private final double PERMIT_PER_SECOND = 1;// 创建本地缓存private final Cache<String, RateLimiter> limiterCache = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build();@Around("execution(* com.fgbg.demo.controller..*.*(..))")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {// 构造keySignature signature = proceedingJoinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;String methodName = proceedingJoinPoint.getTarget().getClass().getName() + "." + methodSignature.getName();String key = requestUtils.getCurrentIp() + "->" + methodName;// 获取key对应的RateLimiterRateLimiter rateLimiter = limiterCache.get(key, () -> RateLimiter.create(PERMIT_PER_SECOND));if (! rateLimiter.tryAcquire()) {// 如果不能立刻获取令牌, 说明访问速度大于1 次/s, 触发限流log.warn("访问过快, 触发限流");throw new RuntimeException("访问过快, 触发限流");}log.info("接口放行...");return proceedingJoinPoint.proceed();}
}

RequestUtils.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Component
public class RequestUtils {@Autowiredprivate HttpServletRequest httpServletRequest;public String getCurrentIp() {return httpServletRequest.getHeader("X-Real-IP");}
}

4. 接口测试

接口测试这块就比较随意了,笔者这里采用apifox进行接口测试。因为AOP逻辑是增强所有接口,因此这里选择了项目曾经暴露出的一个查询接口。点击运行,即可开始测试
在这里插入图片描述

5. 测试结果

在这里插入图片描述
2.6s,分别在0,1,2s开始时,允许接口访问。10个请求中通过3个,失败7个,QPS = 1,限流成功

在这里插入图片描述
测试量达到40,QPS维持1,说明代码逻辑基本没有问题,Google yyds

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【iOS】——SDWebImage源码学习

文章目录 一、SDWebIamge简介二、SDWebImage的调用流程SDWebImage源码分析1.UIImageViewWebCache层2.UIViewWebCache层3.SDWebManager层4.SDWebCache层5.SDWebImageDownloader层 一、SDWebIamge简介 SDWebImage是iOS中提供图片加载的第三方库&#xff0c;可以给UIKit框架中的控…

寄快递怕被骗?快来使用闪侠惠递寄快递避免这种麻烦!

我们去快递驿站寄快递的时候&#xff0c;总是快递的工作人员说是多少钱就要花费多少钱&#xff0c;这样不免会出现快递费高昂的局面&#xff0c;万一工作人员谎报了呢。那岂不是我们就要多花钱了呢&#xff0c;所以说究竟有没有一款寄快递的“神器”来便宜寄快递呢&#xff1f;…

有没有批量调节音乐播放速度的方法?调节音频的播放速度

一&#xff0c;引言​ 调节音乐播放的速度是一种有趣而富有创造性的方式&#xff0c;可以改变我们对音乐的感知和体验。通过调整音乐的速度&#xff0c;我们可以创造出全新的听觉效果&#xff0c;让音乐呈现出不同的情感和氛围。二&#xff0c;调节音乐速度的效果 调节音乐速…

《中医临床诊疗术语》数据库

最新版的《中医临床诊疗术语》于2023年3月17日由国家中医药管理局提出的&#xff0c;由国家市场监督管理总局和国家标准化管理委员会共同发布。新版的修订是为落实相关政策文件要求&#xff0c;推进中医医疗服务规范化、标准化管理&#xff0c;提高中医医疗服务标准化水平和管理…

【JavaScript】Promise 语法

前人栽树 许大仙 aexiar.github.io 前言 函数对象和实例对象 函数对象&#xff1a;将函数作为对象使用&#xff0c;简称为函数对象。实例对象&#xff1a;new 构造函数或类产生的对象&#xff0c;称为实例对象。 示例&#xff1a; <template> </template><s…

OpenHarmony实战开发-如何实现发布图片评论功能。

介绍 本示例将通过发布图片评论场景&#xff0c;介绍如何使用startAbilityForResult接口拉起相机拍照&#xff0c;并获取相机返回的数据。 效果图预览 使用说明 通过startAbilityForResult接口拉起相机&#xff0c;拍照后获取图片地址。 实现思路 1.创建CommentData类&…

【LatentDiffusion 代码详解(1)】LatentDiffusion 的 yaml 解读

YAML 文件提供了一种清晰、简洁且易于理解的方式来描述配置信息&#xff0c;特别适用于机器学习模型的超参数调优和实验管理。 以 Latent Diffusion 官方代码仓库中的 https://github.com/CompVis/latent-diffusion/blob/main/configs/autoencoder/autoencoder_kl_32x32x4.yam…

了解 Unity AI:从初学者到高级的综合指南

游戏中的AI是什么? 游戏中的人工智能是指利用人工智能技术使视频游戏中的非玩家角色和实体智能地行动、做出决策、对游戏环境做出反应,并提供引人入胜的动态游戏体验。什么是NPC? NPC 代表“非玩家角色”。NPC 是视频游戏、角色扮演游戏中不受人类玩家控制的角色。它们是计算…

Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案

原文首发链接&#xff1a;Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案 大家好&#xff0c;我是码农先森。 引言 这次实现音视频实时通信的方案是基于 WebRTC 技术的&#xff0c;它是一种点对点的通信技术&#xff0c;通过浏览器之间建立对等连接&#xff0c;实现音频…

java线程(1)

1、多线程启动 有两种启动方式 1、实现Runable接口 2、继承Thread类并且重写run&#xff08;&#xff09;方法 在执行进程中的任务才会产生线程&#xff0c;所以需要实现Runable接口并且重写run&#xff08;&#xff09;方法&#xff0c;然后将Runable的实现对象作为参数传…

官宣:2024第二十届国际铸造件展12月精彩呈现!

Shanghai International Die-casting Casting Expo 2024第二十届上海国际压铸、铸造展览会 2024第二十届上海国际压铸、铸件产品展 时间&#xff1a;2024年12月18-20日 地点&#xff1a;上海新国际博览中心&#xff08;浦东区龙阳路2345号&#xff09; 报名参展&#xff1…

Delphi Xe 10.3 钉钉SDK开发——审批流接口(获取表单ProcessCode)

开发钉钉审批流时&#xff0c;需要用到钉钉表单的Processcode&#xff0c;有两种方法 &#xff1a; 一、手动获取&#xff1a; 管理员后台——审批——找到对应的表单&#xff1a;如图&#xff1a; ProcessCode后面就是了&#xff01; 二、接口获取&#xff1a;今天的重点&a…