首页 > 数据库 >redis 做分布式限流

redis 做分布式限流

时间:2023-04-23 19:35:14浏览次数:42  
标签:redis param lua 限流 key ttl append 分布式

参看来源:

https://blog.51cto.com/u_15708799/5703440

 

测试有效代码:我们要做的是:当并发请求超出了限定阈值时,要延迟请求,而不是直接丢弃  。当然也可以把结果给业务端,看业务端是提醒用户下次,还是延迟处理,还是丢弃。

 

 @Test
    public void testLimitWait() throws InterruptedException {
        ExecutorService pool = Executors.newCachedThreadPool();
        log.info("--------{}", redisTemplate.opsForValue().get("abc"));
        for (int j = 1; j <= 5; j++) {
            int i=j;
            pool.execute(() -> {
                Thread.currentThread().setName( Thread.currentThread().getName().replace("-","_"));
                limitWait("abc", 3, 1);
                log.info(i + ":" + true + " ttl:" + redisTemplate.getExpire("abc", TimeUnit.MILLISECONDS));
                try {
                    // 线程等待,模拟执行业务逻辑
                    Thread.sleep(new Random().nextInt(100));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        pool.shutdown();
        pool.awaitTermination(2,TimeUnit.SECONDS);
    }


    /**
     * 达到限流时,则等待,直到新的间隔。
     *
     * @param key 可以是ip + 当前秒  或者 是 uid +  当前秒  或者 固定的一个入口 key
     * @param limitCount   一定时间内最多访问次数
     * @param limitSecond  给定的时间范围 单位(秒)
     */
    public void limitWait(String key, int limitCount, int limitSecond) {
        boolean ok;//放行标志
        do {
            ok = limit(key, limitCount, limitSecond);
            log.info("放行标志={}", ok);
            if (!ok) {
                Long ttl = redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
                if (null != ttl && ttl > 0) {
                    try {
                        Thread.sleep(ttl);
                        log.info("sleeped:{}", ttl);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        } while (!ok);
    }


    /**
     * 限流方法    true-放行;false-限流
     *
     * @param key
     * @param limitCount
     * @param limitSecond
     * @return
     */
    public boolean limit(String key, int limitCount, int limitSecond) {
        List<String> keys = Collections.singletonList(key);
        String luaScript = buildLuaScript();
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        Long count = redisTemplate.execute(redisScript, keys, limitCount, limitSecond);
        log.info("Access try count is {} for key = {}", count, key);
        if (count != null && count.intValue() <= limitCount) {
            return true;//放行
        } else {
            return false;//限流
//            throw new RuntimeException("You have been dragged into the blacklist");
        }
    }


    /**
     * 编写 redis Lua 限流脚本
     */
    public String buildLuaScript() {
        StringBuilder lua = new StringBuilder();
        lua.append("local c");
        lua.append("\nc = redis.call('get',KEYS[1])");
        // 实际调用次数超过阈值,则直接返回
        lua.append("\nif c and tonumber(c) > tonumber(ARGV[1]) then");
        lua.append("\nreturn c;");
        lua.append("\nend");
        // 执行计算器自加
        lua.append("\nc = redis.call('incr',KEYS[1])");
        lua.append("\nif tonumber(c) == 1 then");
        // 从第一次调用开始限流,设置对应键值的过期
        lua.append("\nredis.call('expire',KEYS[1],ARGV[2])");
        lua.append("\nend");
        lua.append("\nreturn c;");
        return lua.toString();
    }

 

标签:redis,param,lua,限流,key,ttl,append,分布式
From: https://www.cnblogs.com/chen-msg/p/17347488.html

相关文章

  • redis安装及常用命令
    1.正文--redis[重点]1.什么是NOSQL2.NOSQL和RDBMS的区别3.NOSQL的产品4.介绍Redis5.安装Redis6.redis支持的数据类型2.什么是NOSQLNOSQL(notonlysql)不仅是sql,它是所有非关系数据库的一种统称。3.NOSQL和RDBMS的区别RDBMS--关系型数据库高度组织化结构......
  • redis-哨兵
    1.哨兵高可用主从存在的问题:主从复制,主节点发生鼓掌,可以手动转移,让其中一个slave变成master,这就是哨兵主从复制,只能是主来写数据,所以写的能力和存储能力有限,需要做集群哨兵:sentinel哨兵就是一个redis进程哨兵的工作原理:1.多个sentinel发现并确认master有问题2.选取出一个s......
  • 《Redis设计与实现》读书笔记
    《Redis设计与实现》读书笔记简单动态字符串SDS的定义结构:buf数组:用于保存字符串len属性:记录SDS中保存字符串的长度free属性:记录buf中未使用字节数量遵循C字符串以空字符串结尾的惯例,保存空字符串的字节不计入长度SDS与C字符串的区别常数复杂度获取字符串长度因为SDS中......
  • Ubuntu安装Redis7
    去官网下载Redis最新安装包(sudoapt-getremoveredis-server)此命令可以卸载通过apt安装的redisRedis官网地址:Redis      debhttp://archive.ubuntu.com/ubuntu/trustymainuniverserestrictedmultiverse   步骤四打开服务:serviceredisdst......
  • 分布式发布订阅消息系统 Kafka
    kafka是一种高吞吐量的分布式发布订阅消息系统,她有如下特性:通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。高吞吐量:即使是非常普通的硬件kafka也可以支持每秒数十万的消息。支持通过kafka服务器和消费机集群来分区消息。支......
  • Go--连接redis
    直接看代码packagemainimport("errors""fmt""github.com/go-redis/redis""time")//先下载三方依赖包,以下两个都可以,这里使用的是第一个//goget-ugithub.com/go-redis/redis//goget-ugithub.com/garyburd/redigo/redisfun......
  • redis linux下安装 redis启动方式 redis典型场景 redis通用命令 数据结构和内部编码 r
    内容回顾#dockerfile命令 RUNCOPYADDENVEXPOSEWORKDIRCMD:可以用新命令覆盖的ENTRYPOINT:不可以被覆盖#容器要运行,必须有个前台进程#dockerfile部署图书管理系统项目 FROMpython:3.8MAINTAINERlqzWORKDIR/soft......
  • redis,持久化RDB,AOF,混合(AOF),主从复制原理和方案,哨兵高可用
    内容回顾#悲观乐观锁: django中如何实现 -悲观锁:mysql行锁表锁-乐观锁:真正修改时,加入限制条件django中事务如何开启 -原生sql如何开启事务:begin;commit; -django中如何开事务:atomic()commit()for_update是锁表还是锁行如果查......
  • 使用pipeline执行命令遇到redis.Nil的坑
    参考项目kratos_rockscacheredis数据准备关键代码特别注意,使用pipeline的Exec方法,一定要判断一下redis.Nil这个错误:~~~......
  • redis高级-day6——python操作哨兵、python操作集群、缓存优化
    目录一、python操作哨兵二、python操作集群三、缓存优化3.1redis缓存更新策略3.2缓存击穿,雪崩,穿透一、python操作哨兵#高可用架构后---》不能直接连某一个主库了---》主库可能会挂掉,后来它就不是主库了#之前学的连接redis的操作,就用不了了importredisconn=redis.Red......