1. 功能简介

在分布式系统中,限流(Rate Limiting)是一个非常重要的功能,尤其在API调用、高并发请求的场景下,能够有效防止系统被滥用、避免高负载和服务崩溃。分布式限流是指在多个服务实例之间协调请求的访问频率,确保系统在高并发环境下能够稳定运行。

本示例实现了一个基于 Redis 的分布式限流器。限流器可以通过设定某一时间窗口内的最大请求次数来限制某个用户或IP地址的请求频率,避免滥用。

2. 代码实现

1. 添加 Redis 依赖

首先,我们需要在项目中添加 Redis 依赖。在 pom.xml 中添加以下内容:

<dependencies>
    <!-- Lettuce Redis Client -->
    <dependency>
        <groupId>io.lettuce.core</groupId>
        <artifactId>lettuce-core</artifactId>
        <version>6.1.5</version>
    </dependency>
</dependencies>
2. 创建 Redis 配置类
package com.example.ratelimiter;

import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

public class RedisConfig {
    private static final String REDIS_URI = "redis://localhost:6379"; // Redis 连接地址

    private static RedisClient redisClient;
    private static StatefulRedisConnection<String, String> connection;
    private static RedisCommands<String, String> syncCommands;

    static {
        // 创建 Redis 客户端
        redisClient = RedisClient.create(REDIS_URI);
        connection = redisClient.connect();
        syncCommands = connection.sync();
    }

    public static RedisCommands<String, String> getSyncCommands() {
        return syncCommands;
    }

    public static void close() {
        connection.close();
        redisClient.shutdown();
    }
}
3. 创建分布式限流器类

限流器类会在每次请求时检查请求次数,如果在指定时间窗口内超过限制的次数,就拒绝该请求。这里使用 Redis 的 INCRBY 和 EXPIRE 命令实现请求计数和自动过期。

package com.example.ratelimiter;

import io.lettuce.core.api.sync.RedisCommands;

import java.util.concurrent.TimeUnit;

public class DistributedRateLimiter {

    private static final String RATE_LIMIT_PREFIX = "rate_limit:";
    private String key;
    private int maxRequests; // 每个时间窗口内的最大请求次数
    private long timeWindow;  // 时间窗口,单位:秒

    public DistributedRateLimiter(String key, int maxRequests, long timeWindow) {
        this.key = RATE_LIMIT_PREFIX + key;
        this.maxRequests = maxRequests;
        this.timeWindow = timeWindow;
    }

    // 判断当前请求是否被限流
    public boolean isAllowed() {
        RedisCommands<String, String> syncCommands = RedisConfig.getSyncCommands();

        // 记录当前请求时间
        long currentTime = System.currentTimeMillis();

        // 使用 Redis 的 INCRBY 命令,增加请求次数
        String currentRequestCount = syncCommands.incrby(key, 1).toString();

        // 如果是第一次请求,设置过期时间
        if (Long.parseLong(currentRequestCount) == 1) {
            syncCommands.expire(key, timeWindow);  // 设置时间窗口过期时间
        }

        // 如果当前请求次数超过限制,则拒绝请求
        if (Long.parseLong(currentRequestCount) > maxRequests) {
            return false;  // 超过最大请求次数,限流
        }

        return true;  // 允许请求
    }

    // 清除请求计数(可选)
    public void clear() {
        RedisCommands<String, String> syncCommands = RedisConfig.getSyncCommands();
        syncCommands.del(key);
    }
}
4. 创建测试类
package com.example.ratelimiter;

public class RateLimiterTest {

    public static void main(String[] args) throws InterruptedException {
        // 设置最大请求次数为 5 次,时间窗口为 10 秒
        DistributedRateLimiter rateLimiter = new DistributedRateLimiter("user123", 5, 10);

        // 模拟 7 次请求
        for (int i = 1; i <= 7; i++) {
            if (rateLimiter.isAllowed()) {
                System.out.println("Request " + i + " is allowed.");
            } else {
                System.out.println("Request " + i + " is denied (rate limit exceeded).");
            }
            Thread.sleep(1000);  // 每秒发起一次请求
        }

        // 清除计数器(可选)
        rateLimiter.clear();
        System.out.println("Rate limiter cleared.");
    }
}

3. 使用说明

  1. 配置 Redis:首先,确保本地或远程的 Redis 服务已启动,并能够通过 localhost:6379 或其他指定地址进行连接。

  2. 添加依赖:在 pom.xml 文件中添加 Redis 客户端依赖,本文使用 Lettuce(也可以选择 Jedis)。

  3. 创建 RedisConfig 类:该类配置了 Redis 客户端,并提供了获取 Redis 命令的功能。

  4. 实现 DistributedRateLimiter 类:该类封装了分布式限流的核心逻辑。通过 Redis 的 INCRBY 命令和 EXPIRE 命令实现请求计数与自动过期功能。

  5. 测试类 RateLimiterTest:该类模拟了多个请求发起并判断是否允许访问。如果请求次数超过最大限制,则会拒绝该请求。

4. 示例输出

Request 1 is allowed.
Request 2 is allowed.
Request 3 is allowed.
Request 4 is allowed.
Request 5 is allowed.
Request 6 is denied (rate limit exceeded).
Request 7 is denied (rate limit exceeded).
Rate limiter cleared.

在该示例中,user123 的请求限制被设置为每 10 秒最多允许 5 次请求。当请求次数超过 5 次时,后续的请求会被拒绝,直到时间窗口过期。

5. 总结

通过 Redis 实现的分布式限流器能够有效控制服务的访问频率,防止滥用和高并发的请求带来的压力。该方案不仅简单且高效,同时也能在多个服务实例之间共享限流信息,确保系统的稳定性和可伸缩性。

在实际应用中,限流器常用于API接口保护、服务请求控制等场景,尤其是在高并发情况下,能够有效避免单个用户或IP对服务的过度请求,从而保障系统的高可用性。

更多推荐