《Redis应用实例》Java实现(8):速率限制器


用于实现在一定时间内,只允许用户某个动作操作指定次数,超过次数就拒绝。
比如一分钟内每个用户只能访问某个页面10次。

✅ Jedis原子操作示例类AtomicOperationExample

类似速率限制器这类需求,需要使用redis来做原子操作。
AtomicOperationExample用来演示如何完成原子操作。
有2种方式: 事务和管道


import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import java.util.List;

/**
 * Jedis原子操作示例类
 * 演示Redis中的各种原子操作实现方式
 */
public class AtomicOperationExample {
    /**
     * 演示原子操作的方法
     * @param jedis Jedis客户端实例
     */
    public static void demonstrateAtomicOperations(Jedis jedis) {
        System.out.println("\n----- 演示Jedis原子操作 -----\n");
        // 清除可能存在的测试数据
        jedis.del("counter", "user:1:balance", "user:2:balance", "transaction_test");
        // 1. 使用事务(Transaction)示例
        demonstrateTransactions(jedis);
        // 2. 使用管道(Pipeline)示例
        demonstratePipeline(jedis);
    }

    /**
     * 演示使用Redis事务操作
     */
    private static void demonstrateTransactions(Jedis jedis) {
        System.out.println("【2. 事务操作示例】");
        // 设置初始值
        jedis.set("user:1:balance", "100");
        jedis.set("user:2:balance", "50");
        System.out.println("- 转账前 - 用户1余额: " + jedis.get("user:1:balance"));
        System.out.println("- 转账前 - 用户2余额: " + jedis.get("user:2:balance"));
        // 开始事务
        Transaction tx = jedis.multi();
        try {
            // 执行事务中的命令
            tx.decrBy("user:1:balance", 30); // 用户1减少30
            tx.incrBy("user:2:balance", 30); // 用户2增加30
            // 提交事务
            List<Object> results = tx.exec();
            System.out.println("- 事务执行成功: " + results);
        } catch (Exception e) {
            // 事务回滚
            tx.discard();
            System.out.println("- 事务执行失败,已回滚: " + e.getMessage());
        }
        // 查看转账后结果
        System.out.println("- 转账后 - 用户1余额: " + jedis.get("user:1:balance"));
        System.out.println("- 转账后 - 用户2余额: " + jedis.get("user:2:balance"));
        System.out.println("\n结论:事务可以将多个命令作为一个原子单元执行,要么全部成功,要么全部失败。\n");
    }

    /**
     * 演示使用Redis管道操作
     */
    private static void demonstratePipeline(Jedis jedis) {
        System.out.println("【3. 管道操作示例】");
        // 开始管道
        redis.clients.jedis.Pipeline pipeline = jedis.pipelined();
        try {
            // 添加多个命令到管道
            for (int i = 0; i < 5; i++) {
                pipeline.set("transaction_test:" + i, "value" + i);
                pipeline.get("transaction_test:" + i);
            }
            // 执行管道中的所有命令
            List<Object> results = pipeline.syncAndReturnAll();
            System.out.println("- 管道执行结果数量: " + results.size());
            System.out.println("- 第一个命令结果: " + results.get(0));
            System.out.println("- 第二个命令结果: " + results.get(1));
        } catch (Exception e) {
            System.out.println("- 管道执行过程中发生异常: " + e.getMessage());
        }
        System.out.println("\n重要说明:");
        System.out.println("1. 管道操作本身不保证原子性,即使添加了异常处理也不会改变这个特性");
        System.out.println("2. 如果出现异常,部分命令可能已经执行成功,部分可能执行失败,数据可能不一致");
        System.out.println("3. 管道的主要优势是减少网络往返次数,提高吞吐量");
        System.out.println("4. 如果需要原子性保证,应使用事务或Lua脚本而不是管道\n");
        System.out.println("结论:管道可以减少客户端与服务器之间的网络往返次数,提高执行效率,但不保证原子性。\n");
    }
}

✅速率限制器


package com.foxbill.redisinaction.chapter8;

import redis.clients.jedis.Jedis;
import java.util.List;

/**
 * @Description 速率限制器
 */
public class RateLimiter {
    public static void start(Jedis jedis) {
        long remainTimes = 0;
        boolean isPermitted=false;
        for (int i = 0; i < 10; i++) {
            remainTimes = remainTimes(jedis);
            System.out.println("remainTimes:"+remainTimes);
            isPermitted = isPermitted(jedis);
            System.out.println("isPermitted:"+isPermitted);
            try {
                //等待1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /*  判断是否允许执行     */
    public static boolean isPermitted(Jedis jedis) {
        String key = "permit";
        // 开始管道
        redis.clients.jedis.Pipeline pipeline = jedis.pipelined();
        pipeline.incr(key);
        pipeline.expire(key, 60); //设置失效时间为60秒
        // 执行管道中的所有命令
        List<Object> results = pipeline.syncAndReturnAll();
        Long incrResult = (Long)results.get(0); //第一个命令结果
        int maxTimes = 10;
        if (incrResult >= maxTimes) {
            return false;
        }else{
            return true;
        }
    }

    /*剩余时间*/
    public static long remainTimes(Jedis jedis) {
        int maxTimes = 10;
        String key = "permit";
        String s = jedis.get(key);
        if (s == null) {
            return maxTimes;
        }
        long currentTimes = Long.parseLong(s);
        if (currentTimes >= maxTimes) {
            return 0;
        }
        return maxTimes - currentTimes;
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值