• Redis两种方式实现限流


    案例-实现访问频率限制: 实现访问者 $ip 在一定的时间 $time 内只能访问 $limit 次.
    
    • 非脚本实现
    private boolean accessLimit(String ip, int limit, int time, Jedis jedis) {
        boolean result = true;
    
        String key = "rate.limit:" + ip;
        if (jedis.exists(key)) {
            long afterValue = jedis.incr(key);
            if (afterValue > limit) {
                result = false;
            }
        } else {
            Transaction transaction = jedis.multi();
            transaction.incr(key);
            transaction.expire(key, time);
            transaction.exec();
        }
        return result;
    }
    • 以上代码有两点缺陷 
      1. 可能会出现竞态条件: 解决方法是用 WATCH 监控 rate.limit:$IP 的变动, 但较为麻烦;
      2. 以上代码在不使用 pipeline 的情况下最多需要向Redis请求5条指令, 传输过多.

    • Lua脚本实现 
      Redis 允许将 Lua 脚本传到 Redis 服务器中执行, 脚本内可以调用大部分 Redis 命令, 且 Redis 保证脚本的原子性:

      • 首先需要准备Lua代码: script.lua
    --
    -- Created by IntelliJ IDEA.
    -- User: jifang
    -- Date: 16/8/24
    -- Time: 下午6:11
    --
    
    local key = "rate.limit:" .. KEYS[1]
    local limit = tonumber(ARGV[1])
    local expire_time = ARGV[2]
    
    local is_exists = redis.call("EXISTS", key)
    if is_exists == 1 then
        if redis.call("INCR", key) > limit then
            return 0
        else
            return 1
        end
    else
        redis.call("SET", key, 1)
        redis.call("EXPIRE", key, expire_time)
        return 1
    end
    • Java
    private boolean accessLimit(String ip, int limit, int timeout, Jedis connection) throws IOException {
        List<String> keys = Collections.singletonList(ip);
        List<String> argv = Arrays.asList(String.valueOf(limit), String.valueOf(timeout));
    
        return 1 == (long) connection.eval(loadScriptString("script.lua"), keys, argv);
    }
    
    // 加载Lua代码
    private String loadScriptString(String fileName) throws IOException {
        Reader reader = new InputStreamReader(Client.class.getClassLoader().getResourceAsStream(fileName));
        return CharStreams.toString(reader);
    }

    • Lua 嵌入 Redis 优势: 
      1. 减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;
      2. 原子操作: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;
      3. 复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用.
  • 相关阅读:
    Ubuntu安装Apache
    【C#设计模式——创建型模式】抽象工厂模式
    【C#设计模式——创建型模式】工场方法模式
    【C#设计模式——创建型模式】简单工场模式
    JS判断是不是Decimal类型(正则实现)
    JS实现给页面表单设置触发默认按钮
    记录ASP.NET页面表单初始状态(主要是为了前台可以根据这个判断页面是否变动了)
    Js触发ASP.NET Validation控件的验证, 同时获取前台验证结果(不包括CustomValidator)
    根据字段名查找表名
    SqlServer判断数据库、表、存储过程、函数是否存在
  • 原文地址:https://www.cnblogs.com/barrywxx/p/8563533.html
Copyright © 2020-2023  润新知