跳至主要內容

quan-limiter

大约 5 分钟自定义工具配置分布式锁限流

quan-limiter 是一个基于 Spring AOP 框架实现的限制器组件。其内部默认提供了基于 Redisson 实现的限制器执行器。 可用于分布式流量限制等场景。

通过自定义实现限制器的执行器,可改变默认的执行器实现,达到高度定制化的目的。

使用方法

引入工具依赖包。

gradle
dependencies {
    api project(':quan-tools:quan-limiter')
}

yml配置:

提示

若不配置限制器属性,则应用系统默认的属性配置

quan:
  tools:
    limiter:
      # 令牌租赁时间(令牌有效期,当程序执行完成或令牌到期时,清除令牌)默认:1000(单位:毫秒)
      lease-time: 1000
      # 获取令牌最大等待时间 默认:1000(单位:毫秒)
      wait-time: 1000
      # 令牌的前缀(构建令牌的默认前缀)默认:quan:limiter
      token-prefix: quan:limiter
      # 限制器执行器 默认:RedissonLimiterExecutor 执行器(基于Redisson实现)
      executor: cn.javaquan.tools.limiter.executor.redisson.RedissonLimiterExecutor
      # 是否开启限制器 默认:true,当配置为false时,限制器将不生效
      enabled: false
      # 基于 Redisson 实现的限制器执行器
      redisson:
        # 默认启用 Redisson 实现的限制器执行器
        enabled: true

限制器属性配置说明

enabled

  • 类型:boolean
  • 默认: false

是否开启限制器,当配置为false时,限制器将不生效。

tokenPrefix

  • 类型:string
  • 默认: quan:limiter

令牌的前缀。

leaseTime

  • 类型:long
  • 默认: 1000
  • 单位: 毫秒

持有令牌的有效期,当以申请的令牌超过有效期时,令牌自动失效。

waitTime

  • 类型:long
  • 默认: 1000
  • 单位: 毫秒

获取令牌的最大等待时间,当申请获取令牌超过最大等待时间时,令牌获取失败。

executor

  • 类型:Class<? extends LimiterExecutor>
  • 默认:cn.javaquan.tools.limiter.executor.redisson.RedissonLimiterExecutor

自定义限制器执行器,可改变默认的执行器的实现,达到高度定制化的目的。

redisson

基于 Redisson 实现的限制器执行器配置

redisson.enabled

  • 类型:boolean
  • 默认: true

是否启用 Redisson 实现的限制器执行器,当配置为false时,执行器将不生效。

限制器使用示例

/**
 * @author JavaQuan
 * @date 2021/6/19 11:27
 */
@RestController("/test/")
public class Test {

    // 默认获取全路径构建token,令牌过期时间为1秒,获取令牌最大等待时间为1秒
    @Limiter
    @PostMapping("add")
    public void add(User user) {
    }

    // 如果配置了参数,则按照顺序加入到token后末尾
    // 令牌参数示例:cn.javaquan.app#add_userId_phone
    // 令牌示例:quan:limiter:令牌参数的MD5值
    @Limiter(params = {"#user.userId", "#user.phone"})
    @PostMapping("add")
    public void add(User user) {
    }

    /**
     * 配置 nullable 属性,要求 params 是否可以为空。默认: true
     * true: 令牌参数可以为空,即任何情况下,均会创建令牌。
     * false: 令牌参数不可以为空,要求令牌参数必须存在。如果令牌参数不存在,则不会创建令牌;意味着限制器将不生效。
     */
    @Limiter(params = {"#user.userId"}, nullable = false)
    @PostMapping("add")
    public void add(User user) {
    }

    /**
     * 程序执行完成后,是否自动释放令牌。默认: true
     *
     * 自动释放的场景:
     * 1. 当程序执行完成后,自动释放。
     * 2. 当令牌有效期超过 leaseTime 配置后,自动释放。
     *
     * 当配置值为 false 时,必须等待令牌有效期超过 leaseTime 配置后释放。
     *
     * 示例:
     * API执行完成后,必须等待 60 秒才能再次请求。
     *
     * 典型的应用场景:用户根据手机号码获取短信,必须等待一定的时间后才能再次获取短信。
     */
    @Limiter(params = {"#user.userId"}, leaseTime = 60000, automaticReleaseLock = false)
    @PostMapping("add")
    public void add(User user) {
    }

    /**
     * 如果配置了参数,则按照顺序加入到token后末尾
     * 如果配置了数组参数,则将数组
     * 令牌参数示例:
     *  cn.javaquan.app#add_userId_phone:userId1
     *  cn.javaquan.app#add_userId_phone:userId2
     *
     * 令牌示例:
     * quan:limiter:令牌参数userId1的MD5值
     * quan:limiter:令牌参数userId2的MD5值
     */
    @Limiter(params = {"#user.userId", "#user.phone"}, arrayParams = {"#user.userIds"})
    @PostMapping("add")
    public void add(User user) {
    }

    // 手动配置令牌有效期为2000(单位:毫秒),获取令牌等待时间为1000(单位:毫秒)
    // 获取令牌等待时间建议根据程序执行效率评估。避免等待时间过长
    @Limiter(params = {"#user.userId", "#user.phone"}, leaseTime = 2000, waitTime = 1000)
    @PostMapping("add")
    public void add(User user) {
    }

    // 配置令牌桶限流 /// 由于阿里云集群版限制,新版将不启用,后期视情况扩展其它模式实现
    @Limiter(
            // 配置令牌参数
            params = {"#user.userId", "#user.phone"},
            rateParams = {
                    // 10秒内,最多请求1次
                    @RateParam(rate = 1, rateInterval = 10),
                    // 30秒内,最多请求2次(若exclude排除属性,当获取的参数值不为空时,排除当前规则,即则规则无效)
                    @RateParam(rate = 2, rateInterval = 30, exclude = "#user.memberActivity")
            },
            // 关闭默认限流,默认为开启
            enableDefault = false
    )
    @PostMapping("add")
    public void add(User user) {
    }

}

扩展支持

自定义实现限制器执行器


/**
 * 自定义限制器执行器
 * @author JavaQuan
 * @date 2021/6/19 11:55
 */
public class CustomExecutor extends AbstractLimiterExecutor<Object> {

    /**
     * 根据令牌执行限制器,申请执行权限
     *
     * @param token     令牌
     * @param waitTime  获取令牌的最大时间,超时将获取失败,单位:毫秒
     * @param leaseTime 持有令牌的时间,单位:毫秒
     * @return 限制器执行返回的后置处理器
     */
    @Override
    public LimiterPostProcessor<Object> execute(String token, long waitTime, long leaseTime) throws Exception {
        /// TODO 执行自定义的流程
        /// TODO 获取自定义实现限制器执行返回的实例
        final Object instance = Object.newInstance();
        final boolean locked = instance.execute(waitTime, leaseTime);
        return getPostProcessor(locked, lockInstance);
    }

    /**
     * 释放限制器
     *
     * @param instance {@link LimiterPostProcessor#getInstance()}
     * @return 是否释放成功
     */
    @Override
    public boolean release(Object instance) {
        /// TODO 根据自定义实现限制器执行返回的实例,释放限制器
        return false;
    }
}

使用自定义限流执行器

/**
 * @author wangquan
 * @date 2021/6/19 11:27
 */
@RestController("/test/")
public class Test {
    // 使用自定义限流执行器
    @Limiter(executor = CustomExecutor.class)
    @PostMapping("delete")
    public void delete(User user) {
    }
}

常用SPEL 表达式语法

提示

更多高级用法,请参考 Spring SPEL

字面常量:
    #参数名称.属性名称
    例:#user.userId

集合投影:
    #参数名称.集合属性.![#this.对象属性]
    例:#role.perms.![#this.permId]
上次编辑于:
贡献者: wangquan