Guava实现接口限流

本文最后更新于:2025年6月23日 晚上

Guava实现接口限流

guava是google开发的工具包,包含很多工具。今天通过它的RateLimiter+注解来实现接口限流(底层基于令牌桶算法)。

注意:这通常用来实现单体项目的接口限流,分布式应用中,有更高级的sentinel可供选择,可根据需要使用。

Maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
<!-- guava除了限流还有很多其他工具 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>

<!-- AOP代理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

注解类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 全局限流
* @author peter
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {

// 访问次数
int count() default 1;

// 时间
int time() default 1;
}

也可加一个用户级别的限流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 用户级别限流
* @author peter
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimitForUserAgent {

// 访问次数
int count() default 10;

// 时间
int time() default 1;
}

代理切面类

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
import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
* 限流切面
* @author peter
* @date 2025/6/23
*/
@Aspect
@Component
public class RateLimitAspect {

// ConcurrentHashMap受限于java虚拟机,也可选择存储到redis
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();

@Before(value = "@annotation(rateLimit)")
public void checkRateLimit(JoinPoint joinPoint,RateLimit rateLimit) {
String key = joinPoint.getSignature().toLongString();
RateLimiter rateLimiter = limiters.computeIfAbsent(key, k ->
RateLimiter.create(rateLimit.count() / (double) rateLimit.time()));
if (!rateLimiter.tryAcquire()) {
throw new RuntimeException("请求过于频繁");
}
}

/**
* 用户级限流切面
* @param joinPoint
* @param rateLimit
*/
@Before(value = "@annotation(rateLimit)")
public void checkRateLimitForUserAgent(JoinPoint joinPoint,RateLimitForUserAgent rateLimit) {
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
String userAgent = request.getHeader("User-Agent");
System.out.println("userAgent:"+userAgent);
String key = joinPoint.getSignature().toLongString() + ":" +userAgent;
RateLimiter rateLimiter = limiters.computeIfAbsent(key, k ->
RateLimiter.create(rateLimit.count() / (double) rateLimit.time()));
if (!rateLimiter.tryAcquire()) {
throw new RequestFrequenceException("请求过于频繁");
}
}
}

全局异常处理

自定义请求太频繁异常

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
import lombok.Getter;

/**
* 请求太频繁异常
* @author peter
* @date 2025/6/23
*/
@Getter
public class RequestFrequenceException extends RuntimeException{

private final int code = 400;

public RequestFrequenceException(String message) {
super(message);
}

public RequestFrequenceException(String message, Throwable cause) {
super(message, cause);
}

public RequestFrequenceException(Throwable cause) {
super(cause);
}
}

全局异常处理

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
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

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

/**
* 全局异常处理
* @author peter
* @date 2025/6/23
*/
@Slf4j
@RestControllerAdvice
public class MyAdvice {

@ExceptionHandler(RequestFrequenceException.class)
public Object handleException(RequestFrequenceException e) {
log.error("请求过于频繁");
return processResponse(e);
}

// 符合RESTFUL的返回类型
private ResponseEntity<Map<String, Object>> processResponse(RequestFrequenceException e){
Map<String, Object> map = new HashMap<>();
map.put("code", e.getCode());
map.put("message", e.getMessage());
map.put("data", null);

return ResponseEntity.status(e.getCode()).body(map);
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.event.config.RateLimit;
import com.event.config.RateLimitForUserAgent;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders")
public class OrderController {

@RateLimitForUserAgent(count = 1, time = 1)
@PostMapping
public String createOrder() {
Long orderId = System.currentTimeMillis(); // 模拟订单ID
return "Order created with ID: " + orderId;
}
}

Guava实现接口限流
https://superlovelace.top/2025/06/20/Guava实现接口限流/
作者
棱境
发布于
2025年6月20日
更新于
2025年6月23日
许可协议