Java 限制重复请求:实现策略与代码示例

在Web开发中,限制用户在短时间内的重复请求是一种常见的需求,以避免恶意攻击或减轻服务器压力。本文将介绍Java中实现限制重复请求的几种策略,并提供相应的代码示例。

流程图

首先,我们通过一个流程图来概述限制重复请求的基本流程:

flowchart TD
    A[开始] --> B{检查请求}
    B -- 是首次请求 --> C[记录请求时间]
    B -- 非首次请求 --> D[计算时间差]
    D -- 时间差小于阈值 --> E[拒绝请求]
    D -- 时间差大于阈值 --> F[更新请求时间]
    C --> G[处理请求]
    G --> H[结束]
    E --> H

策略概览

  1. 基于时间戳的简单限制:记录每个用户的最后请求时间,如果在短时间内再次请求,则拒绝。
  2. 使用缓存实现限制:利用缓存(如Redis)存储用户的请求次数和时间戳,实现更高效的限制。
  3. 滑动窗口算法:记录用户在一定时间窗口内的请求次数,超过阈值则拒绝。

代码示例

1. 基于时间戳的简单限制

import java.util.HashMap;
import java.util.Map;

public class SimpleRequestLimiter {
    private static final Map<String, Long> lastRequestTime = new HashMap<>();
    private static final long TIME_LIMIT = 1000; // 1秒内只允许一次请求

    public static boolean isAllowed(String userId) {
        Long lastTime = lastRequestTime.get(userId);
        long currentTime = System.currentTimeMillis();

        if (lastTime != null && (currentTime - lastTime < TIME_LIMIT)) {
            return false; // 请求过于频繁
        }

        lastRequestTime.put(userId, currentTime);
        return true;
    }
}

2. 使用Redis实现限制

import redis.clients.jedis.Jedis;

public class RedisRequestLimiter {
    private static final Jedis jedis = new Jedis("localhost", 6379);

    public static boolean isAllowed(String userId) {
        String key = "request:" + userId;
        long currentTime = System.currentTimeMillis();

        Long lastRequestTime = jedis.getSet(key, currentTime + "");
        if (lastRequestTime != null && (currentTime - Long.parseLong(lastRequestTime + "") < 1000)) {
            return false; // 请求过于频繁
        }

        jedis.expire(key, 10); // 设置过期时间为10秒
        return true;
    }
}

3. 滑动窗口算法

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class SlidingWindowLimiter {
    private static final ConcurrentHashMap<String, AtomicInteger> requestCount = new ConcurrentHashMap<>();
    private static final int MAX_REQUESTS = 5; // 5次请求

    public static boolean isAllowed(String userId) {
        AtomicInteger count = requestCount.computeIfAbsent(userId, k -> new AtomicInteger(0));
        if (count.incrementAndGet() > MAX_REQUESTS) {
            count.set(0); // 超过阈值,重置计数器
            return false;
        }
        return true;
    }
}

总结

通过上述代码示例,我们可以看到Java中实现限制重复请求的几种策略。每种策略都有其适用场景和优缺点。基于时间戳的简单限制易于实现,但可能不够精确;使用Redis可以提高性能和扩展性;滑动窗口算法则可以更精确地控制请求频率。

开发者应根据实际需求和资源情况,选择最合适的策略来实现限制重复请求的功能。同时,也要注意合理设置阈值,避免影响正常用户的使用体验。