API 网关设计
问题
如何设计一个高性能的 API 网关?
答案
网关职责
Spring Cloud Gateway 核心
路由配置 + 自定义过滤器
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("order-service", r -> r
.path("/api/orders/**")
.filters(f -> f
.stripPrefix(1) // 去掉 /api 前缀
.requestRateLimiter(rl -> rl // 限流
.setRateLimiter(redisRateLimiter())
.setKeyResolver(userKeyResolver()))
.circuitBreaker(cb -> cb // 熔断
.setName("orderCB")
.setFallbackUri("forward:/fallback"))
)
.uri("lb://order-service")) // 负载均衡
.build();
}
}
请求聚合(BFF 模式)
网关层聚合多个后端接口
@RestController
public class AggregationController {
@GetMapping("/api/home")
public Mono<HomePageVO> homePage(@RequestHeader("X-User-Id") Long userId) {
// 并行调用多个微服务
Mono<UserInfo> user = userClient.getUser(userId);
Mono<List<Order>> orders = orderClient.getRecentOrders(userId);
Mono<List<Product>> recommend = productClient.getRecommend(userId);
return Mono.zip(user, orders, recommend)
.map(tuple -> new HomePageVO(tuple.getT1(), tuple.getT2(), tuple.getT3()));
}
}
灰度发布
根据请求头/用户标签路由到不同版本
@Component
public class GrayFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
// 灰度用户路由到 v2 版本
if (isGrayUser(userId)) {
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-Version", "v2")
.build();
return chain.filter(exchange.mutate().request(request).build());
}
return chain.filter(exchange);
}
}
常见面试问题
Q1: 网关有什么主流选择?
答案:
| 网关 | 语言 | 特点 |
|---|---|---|
| Spring Cloud Gateway | Java | Spring 生态,响应式 |
| Nginx | C | 高性能,静态配置 |
| Kong | Lua/C | 插件丰富,声明式配置 |
| APISIX | Lua | 高性能,动态路由 |
Q2: 网关如何避免成为单点瓶颈?
答案:
- 网关本身无状态,水平扩展
- 前面加 LVS/Nginx 做四层/七层负载均衡
- 限流、熔断保护后端服务
Q3: 网关和 BFF 的关系?
答案:
- 网关:通用功能(路由、鉴权、限流)
- BFF:针对前端的接口聚合和数据裁剪
- 可以在网关内实现 BFF,也可以独立部署