常用的限流工具Guava RateLimiter 或Redisson RRateLimiter

在分布式系统和高并发场景中,限流是一个非常常见且重要的需求。以下是一些常用的限流工具和库,包括它们的特点和使用场景:

1. Guava RateLimiter

Google 的 Guava 库中的 RateLimiter 是一个简单且高效的限流工具,适用于单节点应用。

优点:

  • 易于使用
  • 高效

缺点:

  • 仅适用于单节点环境,不支持分布式限流

示例代码:

import com.google.common.util.concurrent.RateLimiter;

public class GuavaRateLimiterExample {
    public static void main(String[] args) {
        // 创建一个每秒允许10个请求的RateLimiter
        RateLimiter rateLimiter = RateLimiter.create(10.0);
        
        for (int i = 0; i < 20; i++) {
            // 尝试获取一个许可
            if (rateLimiter.tryAcquire()) {
                System.out.println("Acquired permit " + (i + 1));
                // 执行限流操作
            } else {
                System.out.println("Could not acquire permit " + (i + 1));
            }
        }
    }
}

2. Redis + Lua 脚本

使用 Redis 和 Lua 脚本可以实现分布式限流,因为 Redis 本身是一个高性能的分布式缓存和存储系统。

优点:

  • 支持分布式
  • 高性能

缺点:

  • 实现复杂度较高,需要编写和维护 Lua 脚本

示例代码(Lua脚本):

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")

if current + 1 > limit then
    return 0
else
    redis.call("INCRBY", key, 1)
    redis.call("EXPIRE", key, 1)
    return 1
end

Java代码调用Lua脚本:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class RedisRateLimiter {
    private JedisPool jedisPool;

    public RedisRateLimiter(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public boolean tryAcquire(String key, int limit) {
        try (Jedis jedis = jedisPool.getResource()) {
            String luaScript = "..."; // 上述Lua脚本内容
            Object result = jedis.eval(luaScript, 1, key, String.valueOf(limit));
            return result.equals(1L);
        }
    }
}

3. Redisson RRateLimiter

Redisson 是一个基于 Redis 的 Java 客户端,提供了多种分布式数据结构和服务,其中包括 RRateLimiter 用于分布式限流。

优点:

  • 支持分布式
  • 使用方便,集成良好

缺点:

  • 依赖 Redis

示例代码:

import org.redisson.Redisson;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonRateLimiterExample {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");

        RedissonClient redisson = Redisson.create(config);

        RRateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter");
        rateLimiter.trySetRate(RRateLimiter.RateType.OVERALL, 5, 1, RateIntervalUnit.SECONDS);

        for (int i = 0; i < 10; i++) {
            if (rateLimiter.tryAcquire()) {
                System.out.println("Acquired permit " + (i + 1));
            } else {
                System.out.println("Could not acquire permit " + (i + 1));
            }
        }

        redisson.shutdown();
    }
}

      <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
        </dependency>


spring:
  redis:
    redisson:
      config: |
        singleServerConfig:
          address: redis://xxxxx:6383
          connectionPoolSize: 30
          connectionMinimumIdleSize: 30
          password: xxx
          database: 0
          connectTimeout: 1000
          timeout: 500
          retryAttempts: 3
          retryInterval: 1000
        threads: 10
        nettyThreads: 10
        transportMode: NIO



@Component
@Configuration
public class XXXControlConfig {
    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private xxxControlProperties xxxControlProperties;

    /**
     * 拆章滑动窗口
     */
    @Bean
    public RRateLimiter rateLimiter() {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter("xxx");

        rateLimiter.trySetRate(RateType.OVERALL,
                               flowControlProperties.getRate().getInterval(),
                               flowControlProperties.getRate().getPermits(),
                               RateIntervalUnit.MINUTES);

        return rateLimiter;
    }

    /**
     * 试听最大并发数
     */
    @Bean
    public RSemaphore semaphore() {
        RSemaphore semaphore = redissonClient.getSemaphore("xxxxx");
        semaphore.trySetPermits(flowControlProperties.getSemaphore().getPermits());
        return semaphore;
    }




    @Autowired
    private RRateLimiter rateLimiter;



     if(flowControlProperties.getRate().isEnabled() && !rateLimiter.tryAcquire()) {
            throw new SystemException(Code, "系统繁忙,请稍后再试");
        }

4. Bucket4j

Bucket4j 是一个 Java 限流库,提供了灵活的令牌桶算法实现,可用于本地或分布式限流(通过第三方存储如 Hazelcast)。

优点:

  • 灵活
  • 支持多种存储后端

缺点:

  • 配置相对复杂

示例代码:

import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import io.github.bucket4j.Refill;

import java.time.Duration;

public class Bucket4jExample {
    public static void main(String[] args) {
        Bandwidth limit = Bandwidth.classic(10, Refill.greedy(10, Duration.ofSeconds(1)));
        Bucket bucket = Bucket4j.builder().addLimit(limit).build();

        for (int i = 0; i < 20; i++) {
            if (bucket.tryConsume(1)) {
                System.out.println("Acquired permit " + (i + 1));
            } else {
                System.out.println("Could not acquire permit " + (i + 1));
            }
        }
    }
}

这些工具各有优缺点,可以根据你的具体需求选择合适的限流工具。如果你需要分布式限流,推荐使用 Redis 相关的解决方案,如 Redisson 或者自定义 Lua 脚本。如果是单节点应用,Guava 的 RateLimiter 或 Bucket4j 都是不错的选择。

相关推荐
繁依Fanyi2 分钟前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
慧都小妮子13 分钟前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m512717 分钟前
LinuxC语言
java·服务器·前端
IU宝22 分钟前
C/C++内存管理
java·c语言·c++
瓜牛_gn22 分钟前
依赖注入注解
java·后端·spring
hakesashou23 分钟前
Python中常用的函数介绍
java·网络·python
佚先森32 分钟前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html
古月居GYH1 小时前
在C++上实现反射用法
java·开发语言·c++