1. 简介
Spring Cloud Netflix Zuul 是对 Netflix Zuul 的封装,为 Spring Boot 应用提供了 API 网关能力。虽然它提供了许多实用的功能,但默认并不包含请求限流(Rate Limiting)机制。
✅ 好消息是:有一个社区开源项目 Spring Cloud Zuul RateLimit 可以无缝集成进来,帮你轻松搞定限流功能。
本文将带你快速上手这个限流组件,从依赖配置到策略定义,再到自定义扩展,让你在微服务架构中稳如老狗。
2. Maven 依赖配置
除了引入标准的 spring-cloud-starter-netflix-zuul
外,还需要添加限流插件的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
⚠️ 注意:版本号建议根据你当前使用的 Spring Cloud 版本进行匹配,避免踩不必要的坑。
3. 示例 Controller
先写个简单的 Controller 来模拟两个接口:
@Controller
@RequestMapping("/greeting")
public class GreetingController {
@GetMapping("/simple")
public ResponseEntity<String> getSimple() {
return ResponseEntity.ok("Hi!");
}
@GetMapping("/advanced")
public ResponseEntity<String> getAdvanced() {
return ResponseEntity.ok("Hello, how you doing?");
}
}
💡 这里没有做任何限流相关的代码逻辑,因为我们要把限流规则配置在 application.yml
文件中,保持业务代码干净解耦。
4. Zuul 配置属性
接下来,在 application.yml
中添加如下配置:
zuul:
routes:
serviceSimple:
path: /greeting/simple
url: forward:/
serviceAdvanced:
path: /greeting/advanced
url: forward:/
ratelimit:
enabled: true
repository: JPA
policy-list:
serviceSimple:
- limit: 5
refresh-interval: 60
type:
- origin
serviceAdvanced:
- limit: 1
refresh-interval: 2
type:
- origin
strip-prefix: true
配置说明:
zuul.routes
: 定义路由映射。zuul.ratelimit.policy-list
: 为每个服务设置限流策略。limit
: 在刷新周期内允许的最大请求数。refresh-interval
: 刷新时间窗口(秒)。type
: 限流维度,支持:origin
: 按客户端 IP 限流 ✅url
: 按请求路径限流user
: 按用户身份限流- 不填则为全局服务级别限流
📌 上述配置表示:
/greeting/simple
接口每 60 秒最多调用 5 次;/greeting/advanced
每 2 秒只能调用 1 次;
5. 测试限流效果
5.1 请求未超出限流阈值
@Test
public void whenRequestNotExceedingCapacity_thenReturnOkResponse() {
ResponseEntity<String> response = restTemplate.getForEntity(SIMPLE_GREETING, String.class);
assertEquals(OK, response.getStatusCode());
HttpHeaders headers = response.getHeaders();
String key = "rate-limit-application_serviceSimple_127.0.0.1";
assertEquals("5", headers.getFirst(HEADER_LIMIT + key));
assertEquals("4", headers.getFirst(HEADER_REMAINING + key));
assertThat(
parseInt(headers.getFirst(HEADER_RESET + key)),
is(both(greaterThanOrEqualTo(0)).and(lessThanOrEqualTo(60000)))
);
}
正常情况下返回 HTTP 200,并且响应头中包含限流信息:
X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1: 5
X-RateLimit-Remaining-rate-limit-application_serviceSimple_127.0.0.1: 4
X-RateLimit-Reset-rate-limit-application_serviceSimple_127.0.0.1: 60000
✅ 每次请求后剩余次数递减,重置时间也在变化。
5.2 请求超出限流阈值
@Test
public void whenRequestExceedingCapacity_thenReturnTooManyRequestsResponse() throws InterruptedException {
ResponseEntity<String> response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
assertEquals(OK, response.getStatusCode());
for (int i = 0; i < 2; i++) {
response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
}
assertEquals(TOO_MANY_REQUESTS, response.getStatusCode());
HttpHeaders headers = response.getHeaders();
String key = "rate-limit-application_serviceAdvanced_127.0.0.1";
assertEquals("1", headers.getFirst(HEADER_LIMIT + key));
assertEquals("0", headers.getFirst(HEADER_REMAINING + key));
assertNotEquals("2000", headers.getFirst(HEADER_RESET + key));
TimeUnit.SECONDS.sleep(2);
response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
assertEquals(OK, response.getStatusCode());
}
⚠️ 第二次请求会直接被拦截,返回状态码 429 Too Many Requests。
此时响应头如下:
X-RateLimit-Limit-rate-limit-application_serviceAdvanced_127.0.0.1: 1
X-RateLimit-Remaining-rate-limit-application_serviceAdvanced_127.0.0.1: 0
X-RateLimit-Reset-rate-limit-application_serviceAdvanced_127.0.0.1: 268
等刷新时间过去后,再次请求恢复正常。
6. 自定义 Key 生成器
如果你觉得默认的限流 Key 不够灵活,可以自定义一个 RateLimitKeyGenerator
实现类:
@Bean
public RateLimitKeyGenerator rateLimitKeyGenerator(RateLimitProperties properties,
RateLimitUtils rateLimitUtils) {
return new DefaultRateLimitKeyGenerator(properties, rateLimitUtils) {
@Override
public String key(HttpServletRequest request, Route route,
RateLimitProperties.Policy policy) {
return super.key(request, route, policy) + "_" + request.getMethod();
}
};
}
这样生成的 Key 会加上请求方法名,例如:
X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1_GET: 5
📌 Spring Cloud Zuul RateLimit 会自动识别并使用该 Bean。
7. 自定义错误处理
默认使用的是 DefaultRateLimiterErrorHandler
,只记录日志。如果你想自己处理存储或获取限流数据时的异常,可以这样扩展:
@Bean
public RateLimiterErrorHandler rateLimitErrorHandler() {
return new DefaultRateLimiterErrorHandler() {
@Override
public void handleSaveError(String key, Exception e) {
// 自定义保存错误处理逻辑
}
@Override
public void handleFetchError(String key, Exception e) {
// 自定义读取错误处理逻辑
}
@Override
public void handleError(String msg, Exception e) {
// 全局错误处理
}
};
}
和 KeyGenerator 一样,这个 Bean 也会被自动加载生效。
8. 总结
通过本文我们了解了如何在 Spring Cloud Netflix Zuul 网关中集成限流功能,包括:
- ✅ 引入限流依赖
- ✅ 配置限流策略
- ✅ 测试限流行为
- ✅ 自定义 Key 和异常处理逻辑
这在高并发场景下是非常实用的安全防护手段,特别是在微服务网关层做统一控制。
📦 示例代码可参考 GitHub:https://github.com/eugenp/tutorials/tree/master/spring-cloud-modules/spring-cloud-zuul
💡 提示:虽然 Zuul 已经进入维护阶段,但在很多企业级系统中仍广泛使用,尤其是结合限流、鉴权、监控等功能时依然强大。如果新项目推荐考虑 Spring Cloud Gateway。