Redis+注解实现API接口防刷限流

news2024/11/18 23:48:08

前言

在开发分布式高并发系统时有三把利器来保护系统:缓存、降级、限流。

  • 缓存: 缓存的目的是提升系统访问速度和增大系统处理容量;
  • 降级:降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再打开
  • 限流:限流的目的是通过对并发访问/请求进行限流,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理

api接口的限流主要应用场景:

  • 电商系统(特别是6.18、双11等)中的秒杀活动,使用限流防止使用软件恶意刷单;
  • 各种基础api接口限流:例如天气信息获取,IP对应城市接口,百度、腾讯等对外提供的基础接口,都是通过限流来实现免费与付费直接的转换;
  • 被各种系统广泛调用的api接口,严重消耗网络、内存等资源,需要合理限流。

限流实战

一、SpringBoot集成Redis

  • 引入Redis依赖
<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 配置
server:
  port: 8080

spring:
  redis:
    database: 3
    host: 127.0.0.1
    port: 6379
    timeout: 2000   # 连接超时时间(毫秒)
    jedis:
      pool:
        max-active: 200   # 连接池最大连接数(使用负值表示没有限制)
        max-idle: 20      # 连接池中的最大空闲连接
        min-idle: 0       # 连接池中的最小空闲连接
        max-wait: -1      # 连接池最大阻塞等待时间(使用负值表示没有限制)

  • 配置RedisTemplate
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * RedisTemplate相关配置
     * 使redis支持插入对象
     * @author Fang Ruichuan
     * @date 2023-01-07 20:32
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);
        // 设置key的序列化器
        template.setKeySerializer(new StringRedisSerializer());
        // 设置value的序列化器
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        // json转对象类,不设置默认的会将json转为hashmap
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }
}

二、实现限流

  • 添加自定义@AccessLimit注解
@Inherited
@Documented
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {

    // 指定second时间内API请求次数
    int maxCount() default 5;

    // 请求次数的指定时间范围 秒数(redis数据过期时间)
    int second() default 60;
}
  • 编写拦截器
    • 通过路径:ip作为key,访问次数作为value的方式对某一用户的某一请求进行唯一标识
    • 每次访问的时候判断key是否存在,是否count超过了限流的访问次数
    • 若访问超出限制,则应response返回msg:请求过于频繁给前端给予展示
@Component
@Slf4j
@RequiredArgsConstructor
public class AccessLimiterInterceptor implements HandlerInterceptor {

    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
        try {
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                // 获取方法
                Method method = handlerMethod.getMethod();
                // 是否有AccessLimit注解
                if (!method.isAnnotationPresent(AccessLimit.class)) {
                    return true;
                }
                // 获取注解内容信息
                AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
                if (Objects.isNull(accessLimit)) {
                    return true;
                }
                int seconds = accessLimit.second();
                int maxCount = accessLimit.maxCount();

                // 存储key
                String key = request.getRemoteAddr() + ":" + request.getContextPath() + ":" + request.getServletPath();

                // 已经访问的次数
                Integer count = (Integer) redisTemplate.opsForValue().get(key);
                System.out.println("已经访问的次数:" + count);
                if (Objects.isNull(count) || count.equals(-1)) {
                    redisTemplate.opsForValue().set(key, 1, seconds, TimeUnit.SECONDS);
                    return true;
                }
                if (count < maxCount) {
                    redisTemplate.opsForValue().increment(key);
                    return true;
                }
                if (count >= maxCount) {
                    log.warn("请求过于频繁,请稍后再试");
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            log.warn("请求过于频繁,请稍后再试");
        }
        return true;
    }
}
  • 注册拦截器并配置拦截路径和不拦截路径
@Configuration
@RequiredArgsConstructor
public class InterceptorConfig implements WebMvcConfigurer {

    private final AccessLimiterInterceptor accessLimiterInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(accessLimiterInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/static/**", "login.html", "user/login");
    }
}
  • 使用
@Slf4j
@RestController
@RequestMapping("access")
public class AccessLimiterController {

    @GetMapping("test")
    @AccessLimit(maxCount = 3, second = 60)
    public String limit(HttpServletRequest request) {
        log.error("Access Limit Test");
        return "限流测试";
    }
}
  • 访问测试
    在这里插入图片描述

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

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

相关文章

软件测试之维护性测试

维护性测试用于评估系统能够被预期的维护人员修改的有效性和效率的程度&#xff0c;可从模块化、可重用性、易分析性、易修改性、易测试性、易维护性 1)模块化&#xff1a;评估由独立组件组成的系统或计算机程序&#xff0c;其中一个组件的变更对其他组件的影响大小程度&#x…

【Android 车载 App】实现座椅调节控制十字指针的效果

【Android 车载 App】实现座椅调节控制十字指针的效果效果展示实现方法思路代码第一步&#xff0c;画两条十字虚线第二步&#xff0c;在每个虚线的末端画出圆角三角形第三步&#xff0c;在十字虚线的中间位置画出两个同心圆&#xff0c;一个填充内容&#xff0c;一个描边第四步…

一款新兴的操作pcap的神器

一 前言有机会接触到这款软件&#xff0c;还是同事的一个图&#xff0c;图介绍了开源项目Zed和基于Zed做的一款全流量安全产品Brim。整个产品其实是不少开源项目的一个小集成&#xff0c;只所以感兴趣&#xff0c;除了brim在github有1.5k个star的原因外&#xff0c;更吸引我的是…

缓存的数据一致性

文章目录1.先更新缓存&#xff0c;再更新DB2.先更新DB&#xff0c;再更新缓存3.先删除缓存&#xff0c;后更新DB4.先更新DB&#xff0c;后删除缓存 &#xff08;推荐&#xff09;前言&#xff1a; 只要使用到缓存&#xff0c;无论是本地内存做缓存还是使用 redis 做缓存&#…

Neo4j网页服务器端口Cypher操作直接创建知识图谱

案例1:创建新节点、关系 CREATE (n:美国企业家{name:马斯克}) MERGE (n) <-[:主公]- (n1:总统{name:拜登}) RETURN *创建其他节点的时候,可以一次输入:例如 CREATE

代码随想录day32

第32天 前言 终于到周六了&#xff0c;明天可以休息了&#xff0c;哈哈哈 122. 买卖股票的最佳时机 II 题目 给定一个数组&#xff0c;它的第 i 个元素是一支给定股票第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易&#xff08;多…

DenseNet详解

入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 ✨完整代码在我的github上&#xff0c;有需要的朋友可以康康✨ https://github.com/tt-s-t/Deep-Learning.git 目录 一、DenseNet网…

深度学习——语言模型(笔记)

语言模型&#xff1a;NLP经典的模型 1.语言模型 ①长度为T的文本序列中词元依次是x1,…,xT&#xff0c;xT被认为是文本序列在时间t处的观测或标签。在给定文本序列&#xff0c;语言模型的目标是估计序列的联合概率p(x1,…,xT) ②序列模型的核心是整个序列文本所出现的概率 应…

国家基础地理信息中心行政边界等矢量数据免费下载保姆级教程--关于地理数据收集与处理的基本工具推荐(7)

关于地理数据收集与处理的基本工具推荐系列&#xff0c;有导航&#xff0c;不迷路&#xff1a; 关于地理数据收集与处理的基本工具推荐(1) —高分辨率卫星影像数据免费下载方式关于地理数据收集与处理的基本工具推荐(2)—10m精度的全球土地覆盖数据下载关于地理数据收集与处理…

勿忘2022,迎接2023

2022真的可以说是很不平凡的一年&#xff0c;很多想做的事情也因为一些原因没有做成。不过2022年已经过去&#xff0c;一年一度的总结还是要来写的。废话不多说&#xff0c;还是定关键词。2017年是“小确幸”和“在路上”&#xff0c;感谢师兄师姐的帮助&#xff0c;接触了很多…

write和fwrite

如果只是普通地以O_RDWR的flag去open一个文件朝里write&#xff08;不考虑创建、扩增&#xff09;&#xff0c;那默认内核会把文件的这个页面读进来缓存在内核里的&#xff0c;也即所谓的page cache。随后再发起新的write syscall写相同的页面时&#xff0c;只要写在page cache…

【博学谷学习记录超强总结,用心分享|产品经理基础总结和感悟13】

这里写目录标题第一章、概述第二章&#xff0c;内容服务产品分析框架&#xff1a;用户-平台-创作者内容服务平台优化思考第一章、概述 在分析文字类内容产品之前&#xff0c;我们先来思考一下内容产品的本质是什么&#xff1f;笔者认为&#xff0c;所有满足用户需求的信息服务…

aws beanstalk 使用docker平台部署beanstalk应用程序

参考资料 使用 Docker 平台分支 之前的文章分享过如何使用eb cli工具创建application和eb环境&#xff0c;本文介绍beanstalk支持的docker容器部署 关于beanstalk环境创建相关的资源和部署逻辑&#xff0c;参考之前的文章《aws beanstalk 使用eb cli配置和启动环境》 $ eb …

指南帮手——协议栈

通过 DNS 获取到 IP 后&#xff0c;就可以把 HTTP 的传输工作交给操作系统中的协议栈。协议栈的内部分为几个部分&#xff0c;分别承担不同的工作。上下关系是有一定的规则的&#xff0c;上面的部分会向下面的部分委托工作&#xff0c;下面的部分收到委托的工作并执行。应用程序…

PyTorch源码编译(windows)

1.打开pytorch源码仓库: https://github.com/pytorch/pytorch#from-source2.PyTorch用途与安装方法:3.Python与编译器版本要求 (Python3.7或者更高,编译器要求支持C17)4.如果要支持CUDA编程,要安装NVIDIA CUDA 11或者更高版本, 安装NVIDIA cuDNN v7或者更高版本注:CUDA不支持Ma…

使用 Flask 快速部署 PyTorch 模型

对于数据科学项目来说&#xff0c;我们一直都很关注模型的训练和表现&#xff0c;但是在实际工作中如何启动和运行我们的模型是模型上线的最后一步也是最重要的工作。 今天我将通过一个简单的案例&#xff1a;部署一个PyTorch图像分类模型&#xff0c;介绍这个最重要的步骤。 …

用一串Python代码爬取网站数据

如觉得博主文章写的不错或对你有所帮助的话&#xff0c;还望大家多多支持呀&#xff01;关注、点赞、收藏、评论。 目录一.编码问题二、文件编码三、基本方法四、登录五、断线重连六、正则匹配Excel操作转换网页特殊字符一.编码问题 因为涉及到中文&#xff0c;所以必然地涉及…

MV*系列架构模型

下文仅代表个人理解&#xff0c;可能会有偏差或错误&#xff0c;欢迎评论或私信讨论。 MVC 从软件架构模型角度 MVC 是比较“古老”的架构模型&#xff0c;后面的 MV* 都是基于它进行拓展。MVC 出现的意义是为了提高程序的可维护性与拓展性。在 View 层与 Model 层中添加了 C…

如何分享让人眼前一亮的代码

作为一名软件工程师&#xff0c;会经常需要在工作和写作中粘贴代码片段以作示例&#xff0c;如果不关注代码的格式随手一粘&#xff0c;别人看到的画风就可能是这样&#xff1a;那么&#xff0c;该如何才能快速且优雅地分享代码片段呢&#xff1f;Raycast ray.so 或许是一个值…

2020-12-31 学习74HC595真值表与时序图

考资料教你74hc595时序图怎么看知识详解 - 电子常识 - 电子发烧友网 74HC595是串行输入并行/串行输出的移位锁存器。SHCP是移位脉冲&#xff0c;前沿&#xff08;上升沿&#xff09;有效&#xff0c;STCP是锁存脉冲&#xff0c;前沿有效&#xff0c;DS是输入信号&#xff0c;M…