spring-redis 调用lua脚本实现系统限流

由于业务需要。需要设计服务限流。规则为按秒(QPS)或者按天(QPD)或者其他规则。现在设计如下:

 讲appkey+method作为redis key 。value为限流规则(Hash表类型数据)。存储在reis中

 

  List<BopRateLimitConfig> bopRateLimitConfigs = bopRateLimitConfigService.getAllBopRateLimitConfig();
            if(null != bopRateLimitConfigs && bopRateLimitConfigs.size()>0){
                Map<String,List<String>> configMap = new HashMap<String,List<String>>();//限流配置对象,相同的appkey+method可能有多种时间限流配置,所以创建hashmap来存储相同的配置,value为配置内容
                for (BopRateLimitConfig config : bopRateLimitConfigs) {
//                    value.set("allRateLimitConfigs:" + config.getAppkey()+ "-" + config.getMethod() + "-" + config.getExpiretime(),config.toString());
                    String key = config.getAppkey() + "-" + config.getMethod();
                    if(configMap.containsKey(key)){
                        configMap.get(key).add(config.toString());//相同的appkey+method有不同的限流配置。需加到同一个key的集合中;
                    }else{
                        List<String> configs = new ArrayList<String>();
                        configs.add(config.toString());
                        configMap.put(key,configs);//存储配置
                    }
                }
                HashOperations hashMapValueOperations = redisTemplate.opsForHash();
                hashMapValueOperations.putAll("allRateLimitConfigs",configMap);//入redis,存放hash类型数据

接下来根据配置的规则来判断是否限流:

 HashOperations hashMapValueOperations = redisTemplate.opsForHash();
        Object o = hashMapValueOperations.get("allRateLimitConfigs",appkey + "-" + method);
        if(null == o){
            return true;
        }
        List<String> configStrList = (List)o;
        for(String str : configStrList){
            BopRateLimitConfig bopRateLimitConfig = JacksonUtil.jsonStrToObject(str,BopRateLimitConfig.class);
            if(!rateLimitForLua(bopRateLimitConfig.getAppkey(),bopRateLimitConfig.getMethod(),bopRateLimitConfig.getExpiretime(),bopRateLimitConfig.getThreshhold())){
                return false;
            }
        }
        return true;

其中 rateLimitForLua函数是具体调用lua脚本的函数,

try {
            DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/appkeyAndMethodLimiter.lua")));
            redisScript.setResultType(Boolean.class);
            //组装key值
            String key = getRedisKey(appkey,method,expeireTime,threshhold);
            return redisTemplate.execute(redisScript, Collections.singletonList(key), expeireTime.toString(), threshhold.toString());
        } catch (Exception e) {
            logger.error("ratelimit 出现异常默认放行,appkey:{},method:{}",appkey,method,e);
            return  true;//出现异常 默认放行
        }

 

至此 结束!

 

lua脚本代码:
 

-- key:redis中存放的key,由java程序组装传入, expiretime: 过期时间(单位秒), threshhold :阈值
local key,expiretime,threshhold  = KEYS[1], ARGV[1], ARGV[2];
local currentValue=redis.call("incr", key);-- 获取redis中现有的访问次数
redis.log(redis.LOG_NOTICE, "incr "..key);
if(not currentValue or tonumber(currentValue) == 1) then  -- 如果是nil 需要先set key 。值为1.再设置过期时间
    redis.call("expire", key,  tonumber(expiretime));
    redis.log(redis.LOG_NOTICE, "expire "..key.." "..tonumber(expiretime))
    return true;
else
    return tonumber(currentValue) <= tonumber(threshhold);
end

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值