• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    基于Redis+Lua脚本实现分布式限流组件封装的方法

    创建限流组件项目

    pom.xml文件中引入相关依赖

     dependencies>
     dependency>
     groupId>org.springframework.boot/groupId>
     artifactId>spring-boot-starter-data-redis/artifactId>
     /dependency>
     
     dependency>
     groupId>org.springframework.boot/groupId>
     artifactId>spring-boot-starter-aop/artifactId>
     /dependency>
     
     dependency>
     groupId>com.google.guava/groupId>
     artifactId>guava/artifactId>
     version>18.0/version>
     /dependency>
     
     /dependencies>

    在resources目录下创建lua脚本  ratelimiter.lua

    --
    -- Created by IntelliJ IDEA.
    -- User: 寒夜
    --
     
    -- 获取方法签名特征
    local methodKey = KEYS[1]
    redis.log(redis.LOG_DEBUG, 'key is', methodKey)
     
    -- 调用脚本传入的限流大小
    local limit = tonumber(ARGV[1])
     
    -- 获取当前流量大小
    local count = tonumber(redis.call('get', methodKey) or "0")
     
    -- 是否超出限流阈值
    if count + 1 > limit then
     -- 拒绝服务访问
     return false
    else
     -- 没有超过阈值
     -- 设置当前访问的数量+1
     redis.call("INCRBY", methodKey, 1)
     -- 设置过期时间
     redis.call("EXPIRE", methodKey, 1)
     -- 放行
     return true
    end

    创建RedisConfiguration 类

    package com.imooc.springcloud;
     
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.core.script.DefaultRedisScript;
     
    /**
     * @author 寒夜
     */
    @Configuration
    public class RedisConfiguration {
     
     @Bean
     public RedisTemplateString, String> redisTemplate(
     RedisConnectionFactory factory) {
     return new StringRedisTemplate(factory);
     }
     
     @Bean
     public DefaultRedisScript loadRedisScript() {
     DefaultRedisScript redisScript = new DefaultRedisScript();
     redisScript.setLocation(new ClassPathResource("ratelimiter.lua"));
     redisScript.setResultType(java.lang.Boolean.class);
     return redisScript;
     }
     
    }

    创建一个自定义注解 

    package com.hy.annotation;
     
    import java.lang.annotation.*;
     
    /**
     * @author 寒夜
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface AccessLimiter {
     
     int limit();
     
     String methodKey() default "";
     
    }

    创建一个切入点

    package com.hy.annotation;
     
    import com.google.common.collect.Lists;
    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.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.core.script.RedisScript;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
     
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.stream.Collectors;
     
    /**
     * @author 寒夜
     */
    @Slf4j
    @Aspect
    @Component
    public class AccessLimiterAspect {
     
     private final StringRedisTemplate stringRedisTemplate;
     
     private final RedisScriptBoolean> rateLimitLua;
     
     public AccessLimiterAspect(StringRedisTemplate stringRedisTemplate, RedisScriptBoolean> rateLimitLua) {
     this.stringRedisTemplate = stringRedisTemplate;
     this.rateLimitLua = rateLimitLua;
     }
     
     
     
     @Pointcut(value = "@annotation(com.hy.annotation.AccessLimiter)")
     public void cut() {
     log.info("cut");
     }
     
     @Before("cut()")
     public void before(JoinPoint joinPoint) {
     // 1. 获得方法签名,作为method Key
     MethodSignature signature = (MethodSignature) joinPoint.getSignature();
     Method method = signature.getMethod();
     
     AccessLimiter annotation = method.getAnnotation(AccessLimiter.class);
     if (annotation == null) {
     return;
     }
     
     String key = annotation.methodKey();
     int limit = annotation.limit();
     
     // 如果没设置methodkey, 从调用方法签名生成自动一个key
     if (StringUtils.isEmpty(key)) {
     Class[] type = method.getParameterTypes();
     key = method.getClass() + method.getName();
     
     if (type != null) {
     String paramTypes = Arrays.stream(type)
      .map(Class::getName)
      .collect(Collectors.joining(","));
     log.info("param types: " + paramTypes);
     key += "#" + paramTypes;
     }
     }
     
     // 2. 调用Redis
     boolean acquired = stringRedisTemplate.execute(
     rateLimitLua, // Lua script的真身
     Lists.newArrayList(key), // Lua脚本中的Key列表
     Integer.toString(limit) // Lua脚本Value列表
     );
     
     if (!acquired) {
     log.error("your access is blocked, key={}", key);
     throw new RuntimeException("Your access is blocked");
     }
     }
     
    }

    创建测试项目

    pom.xml中引入组件

    application.yml配置

    spring:
     redis:
     host: 192.168.0.218
     port: 6379
     password: 123456
     database: 0
     application:
     name: ratelimiter-test
    server:
     port: 10087

    创建controller

    package com.hy;
     
    import com.hy.annotation.AccessLimiter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    /**
     * @author 寒夜
     */
    @RestController
    @Slf4j
    public class Controller {
     
     private final com.hy.AccessLimiter accessLimiter;
     
     public Controller(com.hy.AccessLimiter accessLimiter) {
     this.accessLimiter = accessLimiter;
     }
     
     @GetMapping("test")
     public String test() {
     accessLimiter.limitAccess("ratelimiter-test", 3);
     return "success";
     }
     
     // 提醒! 注意配置扫包路径(com.hy路径不同)
     @GetMapping("test-annotation")
     @AccessLimiter(limit = 1)
     public String testAnnotation() {
     return "success";
     }
     
    }

    开始测试,快速点击结果如下

    到此这篇关于基于Redis+Lua脚本实现分布式限流组件封装的方法的文章就介绍到这了,更多相关Redis+Lua脚本实现分布式限流组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • springboot+redis 实现分布式限流令牌桶的示例代码
    • Redis和Lua实现分布式限流器的方法详解
    • 基于Redis实现分布式应用限流的方法
    • Redis分布式限流组件设计与使用实例
    上一篇:Redis概述及linux安装redis的详细教程
    下一篇:为啥Redis使用pipelining会更快
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    基于Redis+Lua脚本实现分布式限流组件封装的方法 基于,Redis+Lua,脚本,实现,