Lua脚本实现滑动窗口

Lua脚本实现滑动窗口

一级目录

java 复制代码
import org.junit.jupiter.api.Test;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;

public class ApplicationTempTest {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private String KEY_PREFIX  = "ni:ai:flow:limit:";
    private long WINDOW_MILLISECOND = 60 * 1000L;
    DefaultRedisScript<Long> script = new DefaultRedisScript<>();

    @PostConstruct
    public void scriptInit() {
        script.setScriptText(getFlowLimitLuaScript());
        script.setResultType(Long.class);
    }
        
    @Test
    public void testA() {
        String userId = "NEW_YY_1566";
        // 用户隔离级别
        // 设计一个滑动窗口算法。一分钟内,task1可以调用5次,task2可以调用10次
        Map<String, Integer> taskMap = new HashMap<>();
        taskMap.put("task1", 5);
        taskMap.put("task2", 10);

        taskMap.forEach((taskId, val) -> {
            boolean allow = allowRequest(taskId, val, WINDOW_MILLISECOND, userId);
            if (allow) {
                System.out.println(taskId + "可以继续执行!");
            } else {
                System.out.println(taskId + "不能继续执行!");
            }
        });
    }

    public boolean allowRequest(String key, long limit, Long windowMillisecond, String userId) {
        List<String> keys = Collections.singletonList(KEY_PREFIX + key);
        List<String> args = Arrays.asList(String.valueOf(limit), String.valueOf(windowMillisecond), String.valueOf(System.currentTimeMillis()), userId);
        Long result = stringRedisTemplate.execute(script, keys, args.toArray());
        // 判断脚本返回结果。限流0,允许1
        return result != null && result == 1;
    }

    public String getFlowLimitLuaScript() {
        return "local key = KEYS[1]\n" +
                "local maxRequests = tonumber(ARGC[1])\n" +
                "local windowMs = tonumber(ARGC[2])\n" +
                "local now = tonumber(ARGC[3])\n" +
                "local user_id = ARGC[4]\n" +
//                "-- 删除过期的记录(时间戳 < now - windows)\n" +
                "redis.call('ZREMRANGEBYSCORE', key, 0, now-windowMs)\n" +
//                "-- 获取当前窗口内的请求数\n" +
                "local count = redis.call('ZCARD', key)\n" +
//                "-- 判断是否超过限制(限流返回0,允许返回1)\n" +
                "if count >= maxRequests then\n" +
                "   return 0\n" +
                "else\n" +
//                "-- 添加当前请求\n" +
                "   redis.call('ZADD', key, now, user_id .. ':' .. now)\n" +
                "   return 1\n" +
                "end";
    }

}
相关推荐
没有bug.的程序员2 小时前
Spring Cloud Gateway:API网关限流与熔断实战
java·开发语言·数据库·spring boot·gateway·api·springcloud
爱学习的阿磊2 小时前
C++代码动态分析
开发语言·c++·算法
Paul_09202 小时前
golang编程题2
开发语言·后端·golang
WWZZ20252 小时前
C++:STL(容器deque)
开发语言·c++·算法·大模型·具身智能
草莓熊Lotso2 小时前
Linux 进程等待与程序替换全解析:从僵尸进程防治到 exec 函数实战
linux·运维·服务器·开发语言·c++·人工智能·python
代码N年归来仍是新手村成员2 小时前
【Go】从defer关键字到锁
开发语言·后端·golang
亓才孓2 小时前
JVM栈帧和堆存储什么类型的数据的分析
java·开发语言
shengli7222 小时前
C++与硬件交互编程
开发语言·c++·算法
肖。35487870942 小时前
窗口半初始化导致的BadTokenException闪退!解决纯Java开发的安卓软件开局闪退!具体表现为存储中的缓存为0和数据为0。
android·java·javascript·css·html