当前位置:   article > 正文

Java并发编程学习之基于Redis+Lua脚本实现分布式限流_java高并发 redis+lua限流实战

java高并发 redis+lua限流实战

Lua语法介绍

数据类型

  • 推荐使用

    nil 空
    boolean 布尔值
    number 数字
    string 字符串
    table 表

类型声明

  • 常用命令

    不用携带类型

    
        --- 全局变量 
        name = 'felord.cn'
    
        --- 局部变量
        local age = 18
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 注意事项

    Redis脚本在实践中不要使用全局变量,局部变量效率更高

类型转换

  • 转换成字符串

    
    
        local bVar = false;
        print(tostring(bVar)); -- 输出"false"
    
        local num1 = 10;
        local num2 = 10.0;
        local num3 = 10.03;
        print(tostring(num1)); --输出"10"
        print(tostring(num2)); --输出"10"
        print(tostring(num3)); --输出"10.03"
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 转换成数字

    
        local num = tonumber("10");    -- 返回十进制数10
        local num = tonumber("AF",16); -- 返回十六进制数175 
        local num = tonumber("0xA");   -- 返回10
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

执行 Redis 命令

  • 常用命令

    redis.call()和redis.pcall()

    1. redis.call()在执行命令的过程中发生错误时,脚本会停止执行,并返回一个脚本错误,错误的输出信息会说明错误造成的原因

    2. redis.pcall() 出错时并不引发(raise)错误,而是返回一个带 err 域的 Lua 表(table),用于表示错误;

  • 测试样例

    类似Java遇到一个异常,前者会直接抛出一个异常;后者会把异常处理成JSON返回

    
    
        redis> lpush foo a
        (integer) 1
    
        redis> eval "return redis.call('get', 'foo')" 0
        (error) ERR Error running script (call to f_282297a0228f48cd3fc6a55de6316f31422f5d17): ERR Operation against a key holding the wrong kind of value
    
        redis 127.0.0.1:6379> EVAL "return redis.pcall('get', 'foo')" 0
        (error) ERR Operation against a key holding the wrong kind of value
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

EVAL命令

  • 内容介绍

    Redis中使用EVAL命令来直接执行指定的Lua脚本

  • 语法介绍

    
        EVAL luascript numkeys key [key ...] arg [arg ...]
    
    
    
    • 1
    • 2
    • 3
    • 4

    EVAL:命令的关键字。

    luascript:Lua 脚本。

    numkeys:指定的Lua脚本需要处理键的数量,其实就是 key数组的长度

    key:传递给Lua脚本零到多个键,空格隔开,在Lua 脚本中通过 KEYS[INDEX]来获取对应的值,其中1 <= INDEX <= numkeys。键名参数 key [key …] 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1], KEYS[2], 以此类推)。

    arg:是传递给脚本的零到多个附加参数,空格隔开,在Lua脚本中通过ARGV[INDEX]来获取对应的值,其中1 <= INDEX <= numkeys。命令的最后,那些不是键名参数的附加参数 arg [arg …],可以在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1], ARGV[2], 诸如此类)

案例分析

  • 功能描述

    Redis + Lua实现限流

  • Lua脚本

    
    
        -- 获取调用脚本时传入的第一个key值(用作限流的 key)
        local key = KEYS[1]
        -- 获取调用脚本时传入的第一个参数值(限流大小)
        local limit = tonumber(ARGV[1])
        -- 获取计数器的限速区间 TTL
        local ttl = tonumber(ARGV[2])
    
        -- 获取当前流量大小
        local curentLimit = tonumber(redis.call('get', key) or "0")
    
        -- 是否超出限流
        if curentLimit + 1 > limit then
            -- 返回(拒绝)
            return 0
        else
            -- 没有超出 value + 1
            redis.call("INCRBY", key, 1)
            -- 如果 key 中保存的并发计数为 0,说明当前是一个新的时间窗口,它的过期时间设置为窗口的过期时间
            if (curentLimit == 0) then
                redis.call('EXPIRE', key, ttl)
            end
            -- 返回 (放行)
            return 1
        end
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
  • 流程分析

    1. 通过KEYS[1] 获取传入的key参数;

    2. 通过ARGV[1]获取传入的limit参数;

    3. 通过 ARGV[2] 获取限流区间 ttl;

    4. redis.call方法,从缓存中get和key相关的值,如果为null那么就返回0;

    5. 接着判断缓存中记录的数值是否会大于限制大小,如果超出表示该被限流,返回0;

    6. 如果未超过,那么该key的缓存值+1,并设置过期时间为 ttl 秒钟以后,未限流。

  • jedis执行脚本

    
        // 三个参数 1 脚本、2 key集合、3 arg集合
        result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(String.valueOf(limit)));
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

参考链接

  • Redis Lua脚本完全入门

    https://segmentfault.com/a/1190000037518418

  • 我司用了 6 年的 Redis 分布式限流器,可以说是非常厉害了!

    https://mp.weixin.qq.com/s/kyFAWH3mVNJvurQDt4vchA

  • 分布式限流:基于 Redis 实现

    https://pandaychen.github.io/2020/09/21/A-DISTRIBUTE-GOREDIS-RATELIMITER-ANALYSIS/

  • redis lua运用

    https://www.jianshu.com/p/f252de9b2d07

  • shield-ratelimter

    https://github.com/TaXueWWL/shield-ratelimter

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/921521
推荐阅读
相关标签
  

闽ICP备14008679号