springboot限流验证码登录接口(优雅版)

news2024/12/23 1:58:59
 

我们的业务逻辑是如图所示, 

限流思路

我们 实现登录接口之后,我们想像这么一个场景,因为我们的登录接口在我们的拦截器中是放行的,如果这时候有人恶意来爆刷我们的登录接口,那我们的这个接口不就爆掉了吗,但是我们如果因为这一个登录接口就去为我们的整个项目添加限流成本就会显的比较高了,而对单一接口限流的话,我们完全可以用成本更低的 令牌桶来实现

那么什么是令牌桶

令牌桶使用

 而我们使用令牌桶也很简单,只需要引入对应的依赖就行了

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.1-jre</version>
        </dependency>

然后看下面的方法

create方法中是 设定桶中有几个令牌,也就是一秒钟 可以使用的请求有多少次,

 这个方法的意思是在 指定的多少时间内获得令牌,如果获得令牌返回true否则立刻返回false

,那么有了这两个方法,我们可以直接在我们接口中 创建限流器,然后进行限流了,但是这样不优雅,实在是不太优雅

优雅版本

我们为了让我们的增强我们的代码可用性,我们可以使用注解+aop的形式来进行令牌桶的限流,这样以后我们有多个接口的话我们直接加一个注解就可以进行限流了,十分的方便

下面来看

注解配置

package com.example.captchalogin.aop;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface limit {
    //不同的方法有不同的降级策略
    String key() default "";
    /**
     * 最多的访问限制次数   也就是令牌桶中 存放的令牌数量
     */
    double permitsPerSecond () ;

    /**
     * 获取令牌最大等待时间
     */
    long timeout();

    /**
     * 获取令牌最大等待时间,单位(例:分钟/秒/毫秒) 默认:毫秒
     */
    TimeUnit timeunit() default TimeUnit.MILLISECONDS;
    /**
     * 得不到令牌的提示语
     */
    String msg() default "系统繁忙,请稍后再试.";

}

aop配置

package com.example.captchalogin.aop;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class RateLimiting {
   //多线程情况下 访问的接口不同 该map可以记录使用过接口限流的接口,有更好的并发性能
   private final Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();

@Before("@annotation(com.example.captchalogin.aop.limit)")
    public  void rateLimit(JoinPoint joinPoint) throws IOException {
    MethodSignature joinPointSignature = (MethodSignature )joinPoint.getSignature();
    Method joinPointSignatureMethod = joinPointSignature.getMethod();
    limit limit = joinPointSignatureMethod.getAnnotation(limit.class);
    if(limit!=null){
        //查看接口中是否有 ratelimiter
if(!limitMap.containsKey(limit.key())){
    //不包含 就加一个限流器
    RateLimiter rateLimiter = RateLimiter.create(limit.permitsPerSecond());
    limitMap.put(limit.key(), rateLimiter);
    log.info("创建了一个限流器,容量为{}",limit.permitsPerSecond());
}
        //包含该方法的限流器的话
        RateLimiter rateLimiter = limitMap.get(limit.key());
        boolean tryAcquire = rateLimiter.tryAcquire(limit.timeout(), limit.timeunit());
log.info("获得到的令牌{}",tryAcquire);
        if(!tryAcquire){
            log.debug("获取令牌失败{}",limit.msg());
            // 发送HTTP 429 Too Many Requests错误  //正常可以设置一个全局异常处理器 抛出异常返回给前端
            ((HttpServletResponse) joinPoint.getArgs()[0])
                    .sendError(HttpServletResponse.SC_NOT_ACCEPTABLE, limit.msg());
        }
    }
}
}

正常来说 tryacquire 之后应该直接抛出异常返回给异常处理器的,我懒了点,直接中断请求

然后验证码登录接口

 @GetMapping("/captcha")
    @limit(key = "limitCaptcha",permitsPerSecond = 1.0, timeout = 500)
    public void getCaptcha( HttpServletResponse response) throws IOException {
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
        //获得验证码
        String captchaCode = lineCaptcha.getCode();
        //存入reids
        stringRedisTemplate.opsForValue().set(MessageContext.Captcha,captchaCode,60, TimeUnit.SECONDS);
        //获得到图形验证码 作为流返回给浏览器
        lineCaptcha.write(response.getOutputStream());
response.getOutputStream().close();
    }}

然后我们进行jmeter压测,并且查看我们的验证码登录接口

问题分析

可以看到返回了406,前面两次返回的都是200,因为 这个桶的机制是 每秒钟自动产生自己设置的令牌数, 当我们  请求没有发送过来的时候,桶中已经自动生成了一个令牌,当我们一使用,立马桶中又产生了一个令牌,所以会出现的问题就显而易见了,假如我们对这个接口访问频率设置为 每秒钟 5次,当我们一段时间不请求,突然有一段时间来了大量请求,我们的接口访问成功的频率 可能就会是10次,而不是五次。怎么解决呢,只能说没有好的解决方案,只能根据业务来自己进行对应不同算法配置

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

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

相关文章

【车载开发系列】使用J-Flash下载Hex文件

【车载开发系列】使用J-Flash下载Hex文件 【车载开发系列】使用J-Flash下载Hex文件 【车载开发系列】使用J-Flash下载Hex文件概要1. 打开J-Flash软件2. 创建新的工程3. 选择芯片类型4. 打开HEX文件5. 连接芯片6. 擦除目标扇区&#xff08;可选&#xff09;7. 烧写文件到目标扇区…

计算机毕业设计PySpark+Django农产品推荐系统 农产品爬虫 农产品商城 农产品大数据 农产品数据分析可视化 PySpark Hadoop

本科生毕业实习与设计&#xff08;论文&#xff09; 基于协同过滤的农产品推荐系统 Agricultural products recommendation system based on Collaborative filtering 学 院&#xff1a; 机械工程学院&#xff08;楷体_GB2312四号&#xff0c;下同&#xff09; …

pdf怎么转换成word?介绍6个pdf转word免费方法!(超简单)

pdf怎么转换成word&#xff1f;pdf格式因兼容多种平台和操作系统而广受欢迎&#xff0c;非常适合文档存储和共享。然而&#xff0c;编辑 PDF 文件通常需要试用一些其它的软件进行辅助&#xff0c;这让许多用户选择将 PDF 转换为 Word 格式&#xff0c;以方便内容修改。在以下情…

《Kotlin核心编程》2021版复习记录

目录 0 前言1 基础语法1.1 数据类型1.2 数组1.3 集合1.4 遍历数据和集合1.5 函数声明返回值类型1.6 var 和 val 2 高阶函数和lambda表达式2.1 高阶函数2.2 方法和成员引用2.3 链式调用2.4 扩展函数2.5 面向表达式编程2.5.1 when表达式2.5.2 for循环2.5.3 in 2.6 字符串相等 3 面…

【nginx 第一篇章】认识一下 NGINX 服务器

一、简介 Nginx (engine x) 是一个高性能的 HTTP 和反向代理服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。由俄罗斯程序员 Igor Sysoev 开发&#xff0c;并在2004年首次公开发布。Nginx 以其高并发处理能力、低内存消耗、稳定性、丰富的功能集、简单的配置以及低学…

硬件面试经典 100 题(31~50 题)

31、多级放大电路的级间耦合方式有哪几种&#xff1f;哪种耦合方式的电路零点偏移最严重&#xff1f;哪种耦合方式可以实现阻抗变换&#xff1f; 有三种耦合方式&#xff1a;直接耦合、阻容耦合、变压器耦合。直接耦合的电路零点漂移最严重&#xff0c;变压器耦合的电路可以实现…

嘉立创eda泪滴

泪滴https://prodocs.lceda.cn/cn/pcb/tools-teardrop/

【1】设计模式简介

一.什么是设计模式 每一个模式描述了一个在我们周围不断重复发生的问题以及该问题的解决方案的核心。这样&#xff0c;你就能一次又一次地使用该方案而不必做重复劳动。 二.深入理解面向对象 向下&#xff1a;深入理解三大面向对象机制 封装&#xff0c;隐藏内部实现继承&am…

Linux SystemV(共享内存(*)、消息队列、信号量)

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a; Linux 目录 ​编辑 一、共享内存 1、原理 理解&#xff1a; 2、操作具体理解 1.概括 2.创建共享内存 共享内存的生命周期&#xff1f; key是什么&#xff1f; 进程怎么知道&#xff0c;共享内存是…

使用 FlexboxLayout 实现灵活布局

在 Android 开发中&#xff0c;有时我们需要让子视图根据内容和可用空间灵活排列。这时候&#xff0c;FlexboxLayout 是一个非常强大的工具&#xff0c;它类似于 CSS 中的 flexbox&#xff0c;允许我们轻松创建响应式布局。在这篇博客中&#xff0c;我们将详细介绍如何在 Andro…

原创度检测工具,快速检测文章原创值

原创度检测工具是帮助我们快速了解文章质量的工具&#xff0c;它能以最短的时间帮助我们了解到一篇文章的原创值是多少&#xff0c;并且还能帮助我们分析文章中哪些内容质量高&#xff0c;哪些内容质量低&#xff0c;从而对低质量的内容进行修改&#xff0c;达到提升整篇文章质…

Android App开发教学: 利用MediaPipe实现即时脸部侦测功能

前言 在Android开发中&#xff0c;实现即时脸部侦测功能是一个具有挑战性且引人注目的任务。幸运的是&#xff0c;Google的MediaPipe库为我们提供了一个简单且高效的解决方案。MediaPipe是一个开源的跨平台机器学习框架&#xff0c;可以用于各种视觉计算任务&#xff0c;包括脸…

C#泛型委托,约束

一、泛型委托 泛型的委托有很多&#xff0c;但掌握常见的泛型委托应用即可 委托可以定义它自己的类型参数。 引用泛型委托的代码可以指定类型参数以创建封闭式构造类型。 public delegate void Del<T>(T t);//Del<T> 是一个泛型委托 public static void Notify…

音频剪辑怎么剪?这4种裁剪方法快学起来

无论是音乐创作、播客录制还是专业的声音设计&#xff0c;高质量的音频剪辑都是创造引人入胜作品的关键。 然而&#xff0c;对于初学者或是专业人士而言&#xff0c;找到合适的音频剪辑工具&#xff0c;并掌握其使用方法&#xff0c;是通往成功道路上的第一步。 那么&#xf…

Python数据库操作必备:事务隔离级别全解析

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在数据库操作中&#xff0c;事务隔离是保证数据一致性和并发控制的重要机制。事务隔离级别决定了一个事务可以看到其他事务的哪些更改&#xff0c;常见的隔离级别包括未提交读&#xff08;Read Uncommitted&…

Java海康门禁设备 对接人脸下发API

海康门禁机设备API接口对接 引言&#xff1a; 我本人在对接海康门禁机设备的时候遇到了两个 问题1&#xff1a;人脸下发失败的问题&#xff0c;是根据官方的Demo&#xff0c;遇到的问题&#xff0c;可以参考一下 这个问题我排查了很久&#xff0c;最终发现是海康给的Demo里面…

EasyX自学笔记3(割草游戏2)

在上一篇笔记之中我们还留有许多bug&#xff0c;如派蒙不会转头、派蒙是鬼没影子、斜向速度过快、会跑出界外的问题在此一并处理。 在上一章里我们知道需要玩家类、敌人类、子弹类三种&#xff0c;但是其包含的是他们的运行逻辑和变量。而播放动画帧也有许多函数我们也将其封装…

学习Java的日子 Day68 jQuery操作节点,Bootstrap

jQuery 1.jQuery操作DOM DOM为文档提供了一种结构化表示方法&#xff0c;通过该方法可以改变文档的内容和展示形式 在访问页面时&#xff0c;需要与页面中的元素进行交互式的操作。在操作中&#xff0c;元素的访问是最频繁、最常用的&#xff0c;主要包括对元素属性attr、内容…

《Hadoop大数据技术与实践》+ 数仓版本

基础概念 随着数字化时代的到来&#xff0c;数据量的爆炸性增长使得传统的数据处理和分析方法变得不够高效&#xff0c;因此大数据技术应运而生。 数据分类 结构化数据&#xff1a;固定格式的SQL数据库等半结构化数据&#xff1a;json非结构化数据&#xff1a;图片、音视频 …