SpringBoot中如何实现限流,这种方式才叫优雅!

小明:最近我在做项目,遇到了接口被恶意刷新的问题。是不是要在 Spring Boot 中搞个限流?但是,怎么做才能既简单又优雅呢?

老王:嘿嘿,小明,看来你是要挑战一下如何在 Spring Boot 中优雅地实现限流了!限流是一个非常重要的环节,尤其是对于高并发的系统。你可以通过多种方式实现,但是今天我给你推荐几种最优雅的方式,让你的系统既高效又不丑陋。

1. 让我们从简单的内存限流开始

对于一些低并发、简单的应用,内存限流已经足够了,直接通过 Spring Boot 自带的工具就可以实现。而且,这种方式既简单又直接。

小王:内存限流?那不就是把请求数限制在一个固定的范围内嘛。

怎么做?

我们可以通过 Spring Boot 的 AOP(面向切面编程) 技术来实现简单的限流。这样,你就能在不修改太多代码的情况下,动态地控制接口的访问频率。

首先,使用一个自定义的注解 @RateLimit 来标记需要限流的方法。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    int limit() default 5; // 每秒最大请求次数
}

然后,使用 AOP 拦截这些方法,并进行限流判断:

@Aspect
@Component
public class RateLimitAspect {
    private final Map<String, Integer> requestCount = new HashMap<>();

    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        int limit = rateLimit.limit();
        
        // 获取当前时间的秒数作为请求标识
        int currentSecond = LocalDateTime.now().getSecond();

        requestCount.putIfAbsent(methodName + currentSecond, 0);
        if (requestCount.get(methodName + currentSecond) >= limit) {
            throw new TooManyRequestsException("请求过于频繁,请稍后再试!");
        }

        // 增加请求次数
        requestCount.put(methodName + currentSecond, requestCount.get(methodName + currentSecond) + 1);

        return joinPoint.proceed();
    }
}

简单的内存限流,就是通过记录每秒请求次数,并在请求超过限制时抛出异常,轻松搞定。

2. 使用 Redis 来实现分布式限流

当系统规模扩大,单一应用已经无法应对高并发时,内存限流就显得不太够用了。此时,分布式限流就显得尤为重要。Redis 是一个非常适合用来做限流的工具,因为它的性能高,支持高并发,而且可以共享数据。

小王:Redis 限流?就是用 Redis 来记录每个请求的次数吗?

怎么做?

  1. 依赖 Redis:首先,你需要在 Spring Boot 中配置 Redis。

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 创建限流工具类:用 Redis 来记录请求次数,每秒都会更新。

@Component
public class RedisRateLimiter {
    @Autowired
    private StringRedisTemplate redisTemplate;

    public boolean isAllowed(String key, int limit) {
        String currentKey = "rate_limit:" + key + ":" + LocalDateTime.now().getSecond();
        
        // 如果当前请求在 Redis 中不存在,表示首次请求,设置为 1
        Boolean exists = redisTemplate.hasKey(currentKey);
        if (exists == null || !exists) {
            redisTemplate.opsForValue().set(currentKey, "1", 1, TimeUnit.SECONDS);
            return true;
        }

        // 如果当前请求次数大于限流值,拒绝请求
        String count = redisTemplate.opsForValue().get(currentKey);
        if (Integer.parseInt(count) >= limit) {
            return false; // 请求超限
        }

        // 否则,增加请求次数
        redisTemplate.opsForValue().increment(currentKey);
        return true;
    }
}
  1. 在控制器中使用限流

@RestController
public class MyController {
    @Autowired
    private RedisRateLimiter redisRateLimiter;

    @GetMapping("/some-api")
    public String someApi(@RequestParam String userId) {
        if (!redisRateLimiter.isAllowed(userId, 5)) {
            throw new TooManyRequestsException("请求过于频繁,请稍后再试!");
        }
        return "请求成功";
    }
}

这种方式使用 Redis 来存储每秒的请求次数,保证在分布式环境下每个用户或 IP 都可以有独立的限流控制。

3. 使用 Guava 的 RateLimiter 做限流

除了 Redis,还有一个轻量级的限流方式,Guava 的 RateLimiter 类。这个库提供了一个令牌桶算法,非常适合用来做流量控制。

小王:Guava 的 RateLimiter?这听起来很像一个流量“限制器”。

怎么做?

  1. 依赖 Guava:首先,添加 Guava 依赖。

<!-- pom.xml -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1-jre</version>
</dependency>
  1. 创建 RateLimiter 实例

@Configuration
public class RateLimiterConfig {

    @Bean
    public RateLimiter rateLimiter() {
        // 每秒生成 5 个令牌
        return RateLimiter.create(5.0);
    }
}
  1. 在服务中使用限流

@RestController
public class MyController {
    @Autowired
    private RateLimiter rateLimiter;

    @GetMapping("/api")
    public String api() {
        // 尝试获取一个令牌,如果没有令牌就阻塞
        if (!rateLimiter.tryAcquire()) {
            throw new TooManyRequestsException("请求过于频繁,请稍后再试!");
        }
        return "请求成功";
    }
}

Guava 的 RateLimiter 是基于令牌桶算法的,适合于节流控制,避免过高的请求负载。

4. Spring Cloud Gateway 限流

如果你使用的是 Spring Cloud Gateway 作为 API 网关,它也提供了非常优雅的限流解决方案,支持基于请求的 IP、用户、URL 等多种策略。

小王:Spring Cloud Gateway 直接做限流?好像有点高级的样子。

怎么做?

你只需要在 application.yml 中配置限流策略:

spring:
  cloud:
    gateway:
      routes:
        - id: rate_limited_route
          uri: https://example.com
          predicates:
            - Path=/api/**
          filters:
            - name: RequestRateLimiter
              args:
                key: "user"
                redis-rate-limiter.replenish-rate: 5
                redis-rate-limiter.burst-capacity: 10

通过这种配置,Spring Cloud Gateway 会自动管理请求的限流,开发者无需写代码。

总结:

看!限流的方式有很多种,可以根据不同的业务需求和技术栈选择最适合的。无论是简单的内存限流、分布式的 Redis 限流,还是轻量级的 Guava 限流,甚至是通过 Spring Cloud Gateway 提供的网关限流,都是非常优雅且实用的做法。

限流不仅能保护你的系统免受恶意请求的攻击,还能提高系统的稳定性和可用性。想要做一个高效的系统,限流这一步绝对是必不可少的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值