首页 > 数据库 >使用Redis调用Lua脚本的方式对SpringBoot接口进行限流

使用Redis调用Lua脚本的方式对SpringBoot接口进行限流

时间:2024-09-02 19:16:26浏览次数:9  
标签:SpringBoot ip Redis 接口 限流 Limit key public

使用Redis调用Lua脚本的方式对SpringBoot接口进行限流

使用Redis调用Lua脚本的方式对SpringBoot接口进行限流

前言

在日常开发中,限流功能时常被使用,用于对某些接口进行限流熔断,譬如限制单位时间内接口访问次数;或者按照某种规则进行限流,如限制ip的单位时间访问次数等,在SpringBoot中,常用的限流方式有:Lua脚本限流、阿里巴巴开源的限流器Sentinel等等,本篇主要介绍使用Lua脚本进行接口限流。


一、步骤

1、自定义限流注解 Limit.java,用于标注在需要限流的接口上

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Limit {
    /**
     * 资源key,用于自定义限流标识时使用,当limitType为key或key+ip时,key不能为空
     */
    String key() default "";
    /**
     * redis中key的前缀
     */
    String prefix() default "limit_";
    /**
     * 时间,以毫秒为限流单位
     */
    int period();
    /**
     * 限制访问次数
     */
    int count();
    /**
     * 类型
     */
    LimitType limitType();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

2、编写限流类型枚举类 LimitType.java

public enum LimitType {
    /**
     * 自定义key限流,即限制所有用户在单位时间内对同一限流标识标识的接口的访问次数
     */
    KEY,
    /**
     * 根据请求者ip限流,即限制同一ip在单位时间内对限流类型为IP类型接口的访问次数
     */
    IP,
    /**
     * key+ip限流,即自定义一个限流标识,限制同一ip在单位时间内对同一限流标识标识的接口的访问次数
     */
    KEYIP,
    /**
     * 根据方法名限流,即限制所有用户在单位时间内对同一接口的访问次数
     */
    METHOD,
    /**
     * 根据方法名+ip限流,即限制同一ip在单位时间内对同一接口的访问次数
     */
    METHODIP;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

3、编写限流具体实现类 LimitAspect.java,通过AOP方式进行限流

@Aspect
@Order(1)
@Component
public class LimitAspect {
    private final RedisTemplate<Object, Object> redisTemplate;
    public LimitAspect(RedisTemplate<Object, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    @Pointcut("@annotation(com.tsing.bootadmin.common.annotations.Limit)")//这里是限流注解所在的路径名
    public void pointcut() {
    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Limit limitAnnotation = method.getAnnotation(Limit.class);
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String key;
        switch (limitAnnotation.limitType()) {
            case KEY:
                key = limitAnnotation.key();
                break;
            case IP:
                key = ServletUtil.getClientIP(request);
                break;
            case KEYIP:
                key = limitAnnotation.key() + "_" + ServletUtil.getClientIP(request);
                break;
            case METHOD:
                key = method.getName();
                break;
            default:
                key = method.getName() + "_" + ServletUtil.getClientIP(request);
        }
        List<Object> keys = Collections.singletonList(limitAnnotation.prefix() + key);
        RedisScript<Boolean> redisScript = new DefaultRedisScript<>(buildLuaScript(), Boolean.class);
        if (redisTemplate.execute(redisScript, keys, limitAnnotation.count(), limitAnnotation.period())) {
            return joinPoint.proceed();
        } else {
            throw new BusinessException(CommonException.Proxy.SERVER_IS_TOO_BUSY);//此处为达到限流次数的逻辑处理,我这里是直接返回异常给前端,可根据实际情况做调整
        }
    }
    /**
     * lua限流脚本
     */
    private String buildLuaScript() {
        return "local num = redis.call('incr',KEYS[1])\n" +
                "if tonumber(num) == 1 then\n" +
                "redis.call('pexpire',KEYS[1],ARGV[2])\n" +
                "return true\n" +
                "elseif tonumber(num) > tonumber(ARGV[1]) then\n" +
                "return false\n" +
                "else\n" +
                "return true\n" +
                "end";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

4、Controller限流测试,我以登录接口为例,实现每个ip在一秒内只能访问一次登录接口

@PostMapping("/login")
@ApiOperation(value = "用户登录")
@Limit(period = 1000, count = 1, limitType = LimitType.METHODIP)
public ResultData<String> login(@Validated @RequestBody UserLoginReqVo reqVo, HttpServletRequest request) {
	return ResultUtil.success(userService.login(reqVo, request));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

5、Swagger限流测试

正常访问:
在这里插入图片描述
快速连续点击两次:
在这里插入图片描述


总结

如果这篇博客对你有帮助的话,记得给我点个赞,你的鼓励是对我最大的支持!谢谢。◕‿◕。

原文链接:https://blog.csdn.net/qq_42375133/article/details/122935969

标签:SpringBoot,ip,Redis,接口,限流,Limit,key,public
From: https://www.cnblogs.com/sunny3158/p/18393318

相关文章

  • RedisCache redis工具类
    publicclassRedisCache{@AutowiredpublicRedisTemplateredisTemplate;/***缓存的基本对象。IntegerString实体类**@paramkey缓存的键值*@paramvalue缓存的值*@param<T>*@return缓存的对象*/......
  • Redis组件介绍(五)
    写在前面今天继续学习redis后面的知识。Redis哨兵机制哨兵Sentinel机制Sentinel(哨兵)是Redis的高可用性解决方案。由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器。当被监视的主服务器进入下线状态时,Sentine......
  • 谈谈springboot中@Conditional相关注解
    @Conditional是一个元注解 @ConditionalOnClass(xx.class)这是用于修饰一个类的注解。它主要是让你的代码具有兼容性,如在多模块下common模块中有一些仅仅是部分其他模块依赖、需要配置的类(例如rabbitMQ配置类,我相信他不应该被全模块需要,但是他放在常用模块中依旧是最合适的)只......
  • springboot多媒体内容管理系统-计算机毕业设计源码08580
    摘 要随着人类向信息社会的不断迈进,风起云涌的信息时代正掀起一次新的革命,同时计算机网络技术高速发展,网络管理运用也变得越来越广泛。因此,建立一个多媒体内容管理系统(CMS)的设计与优化来管理多媒体内容信息,会使管理工作系统化、规范化,提高管理效率。本课题的研究对象是多媒......
  • springboot中小型酒店管理系统-计算机毕业设计源码02793
    摘要随着互联网和移动技术的快速发展,酒店行业也面临着巨大的变革和机遇。传统的酒店管理方式存在着信息不透明、预订流程繁琐等问题,无法满足现代消费者对便捷、高效、个性化服务的需求。因此,开发中小型酒店管理系统具有重要的意义。本文旨在设计和实现一种功能完善、易用且可......
  • springboot 编程事务的封装
    一、创建事务管理工具类java复制importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importorg.springframework.transaction.PlatformTransactionManager;importorg.springframework.transaction.Transact......
  • Java服务端服务限流:Sentinel与Guava RateLimiter的对比
    Java服务端服务限流:Sentinel与GuavaRateLimiter的对比大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在微服务架构中,服务限流是一种重要的保护机制,用于防止系统过载。Sentinel和GuavaRateLimiter是两种流行的限流工具,它们提供了不同的限流策略和实......
  • 基于SpringBoot+MySQL+SSM+Vue.js的学生选课系统
    获取见最下方名片获取见最下方名片获取见最下方名片演示视频基于SpringBoot+MySQL+SSM+Vue.js的学生选课系统(附论文)技术描述开发工具:Idea/Eclipse数据库:MySQLJar包仓库:Maven前端框架:Vue/ElementUI后端框架:Spring+SpringMVC+Mybatis+SpringBoot文字描......
  • redis-数据结构数据类型
    redis常见数据类型作者:xxxRedis共有5种基本数据类型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。数据类型底层数据结构应用场景StringSDS它可以存储任何数据-字符串、整数、浮点值、JPEG图像、序列化的Ruby对象或您希望它承载的任何其他......
  • springboot足球社区管理系统
    指南......