• redis+lua脚本实现接口限流


    写在前面

    在多线程的情况下对一个接口进行访问,如果访问次数过大,且没有缓存存在的情况下大量的请求打到数据库可能会存在数据库宕机,从而造成服务的不可用性。往往我们需要对其进行限流操作用来保证服务的高可用性,以下介绍下redis限流如何使用。

    lua脚本

    Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua 本身并没有提供对于原子性的直接支持,它只是一种脚本语言,通常是嵌入到其他宿主程序中运行,比如Redis。 在Redis中,执行Lua脚本的原子性是指:整个Lua脚本在执行期间,不会被其他客户端的命令打断。

    特性

    • 轻量级:它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
    • 可扩展:Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。

    lua语法教程

    redis使用Lua脚本

    Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。redis Eval 命令基本语法如下:

    redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...] 
    

    参数说明:

    • script: 参数是一段 Lua 5.1 脚本程序。脚本不必(也不应该)定义为一个 Lua 函数。
    • numkeys: 用于指定键名参数的个数。
    • key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
    • arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。

    Jedis实现接口限流

    首先,我们定义一个lua脚本:

    local key = KEYS[1];
    
    local times = ARGV[1];
    
    local expire = ARGV[2];
    
    local afterval = redis.call('incr',key);
    if afterval ==1 then
        redis.call('expire',key,tonumber(expire) )
        return 1;
    end;
    
    if afterval > tonumber(times) then
        return 0;
    end
    
    return 1;
    

    这个脚本首先定义了三个成员变量用来获取方法中传入的值,redis.call()方法是redis的命令脚本执行,即redis执行incr操作,对key中存储的key值加1操作,如果afterval值等于1时,执行redis的expire,设置key的过期时间,tonumber是将参数值转换为数值,返回,如果加一后的值大于我们传入的规定值时,返回0,进行限流。

    java代码实现限流

        public boolean acquire(String limitKey, int limit, int expire) {
            Jedis jedis = new Jedis("127.0.0.1", 6379);
            DefaultRedisScript redisScript = new DefaultRedisScript<>();
            redisScript.setResultType(Long.class);
            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));
            Long result = (Long) jedis.eval(redisScript.getScriptAsString(), 1, limitKey, String.valueOf(limit), String.valueOf(expire));
            if (result == 0){
                return false;
            }else {
            return true;
            }
        }
    

    controller层调用

         if (isAcquire.acquire("myKey",10,60)){
              // 接口放行
               return "success";
           }else {
             // 接口拒绝
               return "err";
           }
    

    这个方法是传入一个你的key,这个key下每一分钟只能请求10次,如果超出10次,进行限流操作,等到上次请求接口的时间超出1分钟后才可以进行放行操作。

    END

  • 相关阅读:
    opencv python debug记录
    Centos编译安装python3.9
    实用篇-Nacos配置管理
    并查集介绍 & 代码实现 & 优化思路详解
    Android OpenGL ES入门
    Problem A: 两个有序链表序列的合并
    微擎模块 有间解忧杂货店小程序2.14 修复用户头像昵称不显示的BUG
    面向对象(基础)知识点强势总结!!!
    博客积分规则
    Apache Atlas服务安装
  • 原文地址:https://www.cnblogs.com/libinscloud/p/18145150