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

这篇具有很好参考价值的文章主要介绍了【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 什么是限流

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

2. 常见的限流策略

2.1 漏斗算法

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

【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter),前后端的那些事,spring boot,限流,RateLimiter,令牌桶算法,漏斗算法

2.2 令牌桶算法

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

【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter),前后端的那些事,spring boot,限流,RateLimiter,令牌桶算法,漏斗算法

2.3 次数统计

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

【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter),前后端的那些事,spring boot,限流,RateLimiter,令牌桶算法,漏斗算法

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 {
    @Autowired
    private RequestUtils requestUtils;

    // 每秒生成1个令牌, 同个ip访问同个接口的QPS为1
    private 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 {
        // 构造key
        Signature signature = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        String methodName = proceedingJoinPoint.getTarget().getClass().getName() + "." + methodSignature.getName();
        String key = requestUtils.getCurrentIp() + "->" + methodName;

        // 获取key对应的RateLimiter
        RateLimiter 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 {

    @Autowired
    private HttpServletRequest httpServletRequest;

    public String getCurrentIp() {
        return httpServletRequest.getHeader("X-Real-IP");
    }
}

4. 接口测试

接口测试这块就比较随意了,笔者这里采用apifox进行接口测试。因为AOP逻辑是增强所有接口,因此这里选择了项目曾经暴露出的一个查询接口。点击运行,即可开始测试
【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter),前后端的那些事,spring boot,限流,RateLimiter,令牌桶算法,漏斗算法

5. 测试结果

【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter),前后端的那些事,spring boot,限流,RateLimiter,令牌桶算法,漏斗算法
2.6s,分别在0,1,2s开始时,允许接口访问。10个请求中通过3个,失败7个,QPS = 1,限流成功

【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter),前后端的那些事,spring boot,限流,RateLimiter,令牌桶算法,漏斗算法
测试量达到40,QPS维持1,说明代码逻辑基本没有问题,Google yyds文章来源地址https://www.toymoban.com/news/detail-854236.html

到了这里,关于【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【前后端的那些事】开源!快速上手富文本+富文本图片上传

    前言 :最近写项目,发现了一些很有意思的功能,想写文章,录视频把这些内容记录下。但这些功能太零碎,如果为每个功能都单独搭建一个项目,这明显不合适。于是我想,就搭建一个项目,把那些我想将的小功能全部整合到一起。实现 搭一次环境 ,处处使用。 本文主要

    2024年01月20日
    浏览(57)
  • 【前后端的那些事】2万字详解WebRTC + 入门demo代码解析

    WebRTC是一项允许网页浏览器进行实时音视频通信的技术标准。旨在实现在浏览器之间直接进行点对点的音频、视频以及数据共享,无需安装任何插件或额外软件。 ICE全称Interactive Connectivity Establishment ,是一种交互式连接框架,他允许两个设备进行p2p通讯。 在进行p2p通讯过程

    2024年03月11日
    浏览(39)
  • 基于Springboot 限制IP访问指定的网址

    添加一个简单的白名单,然后只有白名单上面的 IP 才能访问网站,否则不能访问 这是只是一个很简单的实现方法 首先:在 application.yml 配置 IP 白名单 想要引用到配置文件里面的 String 数组,如果使用普通的 @Value 是不行的,必须使用其他方法 步骤一:创建一个实体类 这样配

    2024年01月18日
    浏览(46)
  • Nginx 限流模块:限制高并发和IP访问频率

    Nginx 是我们常用的负载均衡和反向代理服务器,并发性能非常优秀。 但是在并发量极大的情况下,必要限流措施还是需要的,Nginx 的有对应的模块插件可通过简单配置来完成这个功能。 1、添加 limit_conn_zone 这个变量只能在http使用。

    2023年04月25日
    浏览(84)
  • 从零开始快速搭建SpringBoot+Mybatis+小程序应用--微信小程序的入门和前后端的联调

    目录 项目介绍  vx小程序简介 VX开发工具介绍 列表页开发 list的编写   列表页前后端联调  信息编辑页开发 operation的编写 区域信息编辑页的联调 从0搭建后端的Springboot+mybatis框架 实现后端的业务功能 实现本地微信小程序的前端开发 前端与后端的调控 技术储备要求 1.基础的

    2024年02月10日
    浏览(65)
  • SpringBoot实现图形验证码功能+访问频率设置+缓存

    目录 1、springboot实现图形验证码生成 1.1、导入Maven依赖 1.2、写一个生成图片的工具类 1.3、编写接口生成验证码并存入Redis 2、实现图形验证码判断是否正确 2.1、编写验证图形验证码接口 2.2、前端代码 2.3、请求发送 3、实现访问频率限制 3.1、创建自定义注解 3.2、创建自定义

    2024年04月17日
    浏览(36)
  • 进度变动实时通知-使用SocketIO实现前后端的通信(基于WebSocket的实时通信库)

    最近在接触的一个项目,将PDF上传到项目里,通过调用OCR云服务把PDF里的表格数据识别出来。在此过程中,前后端需要实时通信,对识别数据进行“进度跟踪”。因此我们采用SocketIO的通讯方式,识别中前端和后端服务建立SocketIO连接,根据事件进行数据的实时更新百分比进度

    2024年02月06日
    浏览(51)
  • SpringBoot限制接口访问频率 - 这些错误千万不能犯

    最近在基于SpringBoot做一个面向普通用户的系统,为了保证系统的稳定性,防止被恶意攻击,我想控制用户访问每个接口的频率。为了实现这个功能,可以设计一个annotation,然后借助AOP在调用方法之前检查当前ip的访问频率,如果超过设定频率,直接返回错误信息。 在开始介

    2024年02月05日
    浏览(44)
  • nginx 前后的分离 (ip/域名)访问 负载均衡

    首先前端随便访问后端的一个端口,后端监听这个端口进行服务转发。 比如:8888 然后nginx在我们的服务器上部署两个后端 这里我用docker部署了两个 当然你也可以在两个服务器上面部署两个后端,只要在nginx配置代理的时候修改一下ip就可以了。 nginx配置

    2024年04月29日
    浏览(36)
  • TCP/IP网络编程(1)——基于TCP的服务端和客户端的简单实现

    目录 前言 一、服务器端函数 1. 创建套接字函数 socket 2. 套接字绑定地址函数 bind 3. 等待连接请求函数 listen 4. 处理连接请求函数 accept 5. 关闭套接字函数 close 二、客户端函数 1. 请求连接函数 connect 三、完整代码 四、 基于TCP的半关闭 shutdown 五、 套接字可选项 getsockopt setsoc

    2024年02月07日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包