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";
    }

}
相关推荐
肉包_5111 天前
两个数据库互锁,用全局变量互锁会偶发软件卡死
开发语言·数据库·c++
大空大地20261 天前
流程控制语句--if语句
开发语言
tb_first1 天前
LangChain4j简单入门
java·spring boot·langchain4j
毕设源码-邱学长1 天前
【开题答辩全过程】以 基于PHP的发热病人管理平台的设计与实现为例,包含答辩的问题和答案
开发语言·php
HellowAmy1 天前
我的C++规范 - 线程池
开发语言·c++·代码规范
独自破碎E1 天前
【BISHI9】田忌赛马
android·java·开发语言
czy87874751 天前
const 在 C/C++ 中的全面用法(C/C++ 差异+核心场景+实战示例)
c语言·开发语言·c++
范纹杉想快点毕业1 天前
实战级ZYNQ中断状态机FIFO设计
java·开发语言·驱动开发·设计模式·架构·mfc
smileNicky1 天前
布隆过滤器怎么提高误差率
java
それども1 天前
分库分表的事务问题 - 怎么实现事务
java·数据库·mysql