• redis + lua实现分布式接口限流实现方案


    前言

    redis + lua脚本已然成为了单体项目主流的限流方案。

    redis凭借其特性成为了中间件的佼佼者,最新官方测试数据:

    • 读的速度是110000次/s
    • 写的速度是81000次/s。

    lua:

    • 减少网络开销:使用Lua脚本,无需向Redis 发送多次请求,执行一次即可,减少网络传输
    • 原子操作:Redis 将整个Lua脚本作为一个命令执行,原子,无需担心并发
    • 复用:Lua脚本一旦执行,会永久保存 Redis 中,,其他客户端可复用

    操作

    在需要限流的接口处添加如下注解(@RedisLimit),在原有基础上,无需添加任何依赖即可实现限流。

    @RedisLimit(name = "订单秒杀", prefix = "seckill", key = "distributed", count = 1, period = 1, limitType = LimitType.IP, msg = "当前排队人数较多,请稍后再试!")
    @GetMapping("/limit/distributed/{id}")
    public ResponseEntity<Object> limitDistributed(@PathVariable("id") String id) {
    
        return ResponseEntity.ok("成功购买:" + id + "个");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    介绍

    /**
     * 资源名称
     */
    String name() default "";
    
    /**
     * 前缀
     */
    String prefix() default "";
    
    /**
     * 资源key
     */
    String key() default "";
    
    /**
     * 最多访问次数
     */
    int count();
    
    /**
     * 时间,秒级
     */
    int period();
    
    /**
     * 类型
     */
    LimitType limitType() default LimitType.CUSTOMER;
    
    /**
     * 提示信息
     */
    String msg() default "系统繁忙,请稍后再试";
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34

    功能

    • 默认根据全局接口的QPS作为流控指标
    • 可在独立IP和全局接口自由切换
    • 可自定义时间及规定时间内的QPS
    • 可根据key值做后期的数据监控和统计

    原理

    • 以流量作为切点、滑动时间窗口作为核心算法。

    • lua脚本以保证其原子性操作

    核心代码(部分展示)

    lua脚本

    redis.replicate_commands();
    local listLen,time
    listLen = redis.call('LLEN', KEYS[1]) 
    if listLen and tonumber(listLen) < tonumber(ARGV[1]) then
    local a = redis.call('TIME');
    redis.call('LPUSH', KEYS[1], a[1]*1000000+a[2]) 
    else
    time = redis.call('LINDEX', KEYS[1], -1)
    local a = redis.call('TIME');
    if a[1]*1000000+a[2] - time < tonumber(ARGV[2])*1000000 then
    return 0;
    else
    redis.call('LPUSH', KEYS[1], a[1]*1000000+a[2])
    redis.call('LTRIM', KEYS[1], 0, tonumber(ARGV[1])-1)
    end
    end
    return 1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    切点处理

    Long number = redisTemplate.execute(SECKILL_SCRIPT, keys, count, period);
    if(number != null && number.intValue() == 1){
                    return pjp.proceed();
                }
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    006_Makefile Study(1)
    软考系统架构设计师考试冲刺攻略
    进程的调度算法
    使用树莓派学习PostgreSQL
    计算机毕业设计Java大连环保公益网(源码+系统+mysql数据库+lw文档)
    redis的配置文件
    【Linux】基础
    ELK入门(一)-Elasticsearch(docker版)
    HRNet人体关键点检测
    免费SSL证书
  • 原文地址:https://blog.csdn.net/CSDN_SAVIOR/article/details/125425747