• 【Java】RedisTemplate来控制某段时间内执行最大次数


    一、前言

    RedisTemplate来控制某段时间内执行最大次数的Java示例代码。

    二、使用场景

    假设我们希望限制某个操作(如发送邮件、访问特定API等)在1小时内最多执行n次。

    三、思路

    1. 使用redis的zset数据结构,将当前时间戳作为分值。
    2. 每次发送短信时,先删除1个小时以前的元素(即分值从0,一个小时前的时间戳)。
    3. 删除过期的元素后再统计,key中的元素个数(1个小时内的发送次数),看是否大于6次。
    4. 发送成功后,将当前时间戳做为值和分值保存在zset中。

    四、代码示例

    一个小时内同一用户最多发送6次短信

    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    import java.util.Set;
    
    import static java.util.stream.Collectors.toList;
    
    @Component
    public class RateLimiter {
    
        private static final String KEY_PREFIX = "rate_limiter:send_msg:";
        private static final long INTERVAL_MS = TimeUnit.HOURS.toMillis(1);
        private static final int MAX_COUNT = 6;
    
        private final RedisTemplate<String, Object> redisTemplate;
    
        public RateLimiter(RedisTemplate<String, Object> redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        public boolean canPerformOperation(String userId) {
            String key = KEY_PREFIX + userId;
    
            // 获取当前时间戳(毫秒)
            long now = System.currentTimeMillis();
    
            // 删除已过期的计数
            long startOfInterval = now - INTERVAL_MS;
            Set<TypedTuple<Object>> expiredCounts = redisTemplate.opsForZSet().rangeByScoreWithScores(key, 0, startOfInterval);
            if (!expiredCounts.isEmpty()) {
                List<Object> expiredKeys = expiredCounts.stream().map(TypedTuple::getValue).collect(toList());
                redisTemplate.opsForZSet().removeRangeByScore(key, 0, startOfInterval);
                System.out.println("Removed " + expiredCounts.size() + " expired counts");
            }
    
            // 获取剩余次数
            long currentCount = redisTemplate.opsForZSet().zCard(key);
            long remainingCount = MAX_COUNT - currentCount;
    
            // 如果剩余次数大于0,则添加新的计数项并返回true
            if (remainingCount > 0) {
                redisTemplate.opsForZSet().add(key, now, now);
                return true;
            } else {
                return false;
            }
        }
    
        // 示例用法:
        public static void main(String[] args) {
            RateLimiter rateLimiter = ...; // 初始化RateLimiter实例
            String userId = "100000";
            if (rateLimiter.canPerformOperation( userId )) {
                System.out.println("Sending msg...");
            } else {
                System.out.println("Rate limit exceeded. Unable to send msg.");
            }
        }
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
  • 相关阅读:
    HiveServer2 Service Crashes(hiveServer2 服务崩溃)
    龙格-库塔(Runge-Kutta)方法C++实现
    ptyhon flask SSE 浏览器和服务器实时通信-例子实时推送随机数到前端画echart曲线图
    Linux中gitlab-runner部署使用备忘
    上半年暂停考试要补考?包含监理工程师、建筑师等十项考试
    【SemiDrive源码分析】【MailBox核间通信】51 - DCF_IPCC_Property实现原理分析 及 代码实战
    匠心传承,长期主义 | 竹云董事长董宁受邀出席大湾区品牌新消费论坛
    【10.26】集美大学第九届程序设计竞赛(同步赛)
    springboot注解开发如何映射对象型数据
    HTML+CSS+JS做一个好看的个人网页—web网页设计作业
  • 原文地址:https://blog.csdn.net/weixin_38538285/article/details/138225000