API网关:微服务的大门守护者
说起API网关,很多面试者第一反应就是“反向代理加负载均衡”。确实,这是个起点,但远远不够。我面试过不少人,能讲清楚网关作用的人不到一半。而能说出Zuul和Gateway核心区别的,更是凤毛麟角。
今天我们就把这个知识点彻底吃透。API网关到底是什么?说白了,它就是微服务架构的外部入口点。所有来自客户端的请求,先打到网关,再由网关转发到后面的具体服务。
API网关的核心作用
网关在微服务架构里扮演着“守门员”的角色。我把它总结为五个核心职责,面试时只要把这五点讲透,基本就稳了。
- 统一入口:所有客户端请求先到网关,再由网关分发。这样外部系统只需要知道一个地址。
- 协议转换:内部服务可能用http,也可能用gRPC或者Dubbo。网关可以完成协议适配。
- 路由转发:根据请求路径、参数、Header等信息,把请求分发到对应的后端服务。
- 横切关注点:认证、授权、限流、熔断、日志、监控,这些通用功能统一在网关层实现。
- 灰度发布:通过路由规则,可以把部分流量引入新版本服务,实现平滑上线。
我在项目中遇到过最典型的场景:最开始服务少,每个服务都暴露自己的端口,运维噩梦。后来上了网关,只要把域名指向网关,路由规则维护好,后面的事情就简单多了。嗯,这里要注意,网关不是万能药,你不能把所有逻辑都塞进去,否则它会变成一个单点瓶颈。
主流实现方式:Zuul vs Gateway
Java生态里,最常用的两种网关方案就是Netflix Zuul和Spring Cloud Gateway。很多人搞不清楚它们的区别,我来给你梳理一下。
| 对比维度 | Zuul 1.x | Spring Cloud Gateway |
|---|---|---|
| 底层模型 | Servlet阻塞式I/O | WebFlux非阻塞响应式 |
| 性能 | 长连接场景效率低 | 高并发场景优势明显 |
| 长连接支持 | 不支持,只能HTTP短连接 | 原生支持WebSocket |
| 路由配置 | 基于路径匹配,相对简单 | 基于路由谓词和过滤器链 |
| 过滤机制 | Filter(pre/route/post/error) | GatewayFilter + GlobalFilter |
| 官方维护状态 | 已进入维护模式(2020年末) | Spring官方积极维护 |
| 社区生态 | 丰富但逐渐向Spring迁移 | 与Spring Cloud深度集成 |
我个人习惯在新项目中首选Spring Cloud Gateway。为什么?因为Zuul从1.x开始就存在阻塞问题。虽然Zuul 2.x也做了非阻塞改造,但那时候Gateway已经站稳了脚跟。Spring官方直接推荐Gateway,咱们就没必要逆流而上了。
路由配置:怎么玩转路径分发
路由是网关的基础功能。你配置的每一条路由,都在告诉网关:“这个请求,应该交给谁处理。”Gateway的配置有两种方式,YAML和Java DSL。我更喜欢YAML,因为配置清晰,好维护。
来看一段典型的路由配置:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
- AddRequestHeader=X-Gateway-Request, user-gateway
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
key-resolver: "#{@userKeyResolver}"
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
这段配置里,我解释几个关键点:
lb://表示负载均衡地址,Gateway会通过服务发现拿到实例列表StripPrefix=1去掉一级路径前缀,比如/api/user/list变成/user/listPath是谓词,表示匹配哪些路径的请求才走这条路由RequestRateLimiter是一个内置的限流过滤器,基于令牌桶算法
为什么不把所有服务写在一个路由里?你想想看,如果所有请求都匹配同一个路由,那就没法做精细化控制。不同的服务有不同的限流配置、认证策略,分路由配置才是最合理的方式。
过滤机制:在请求的路上做手脚
过滤器是网关的灵魂。它让你在请求进来之后、响应出去之前,执行一些自定义逻辑。比如校验Token、改写请求参数、记录日志、加解密等。
Gateway的过滤器分两种:
- 局部过滤器(GatewayFilter):只作用在特定路由上,配置在路由的filters中
- 全局过滤器(GlobalFilter):作用于所有路由,需要写Java代码
我来写一个全局过滤器的示例,用于记录请求耗时:
@Component
public class TimeFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(TimeFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long startTime = System.currentTimeMillis();
String path = exchange.getRequest().getURI().getPath();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
long cost = System.currentTimeMillis() - startTime;
log.info("请求路径: {}, 耗时: {}ms", path, cost);
}));
}
@Override
public int getOrder() {
return -100;
}
}
这段代码其实很简单。关键点在于 getOrder() 方法,返回值越小,优先级越高,执行顺序越靠前。我曾经踩过一个坑:过滤器顺序没控制好,导致认证过滤器比限流过滤器后执行。结果大量非法请求先触发了限流,把正常用户的额度都占用了。自那以后,我一直把认证和限流过滤器的order值严格定义好。
限流策略:保护后端不被打垮
限流是网关的必修课。为什么?因为你永远不知道客户端会发起什么样的流量。一次促销活动,甚至一次恶意攻击,都可能让你的后端服务瞬间崩溃。
常见的限流算法有四种:
| 算法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 计数器 | 单位时间内累计请求数,超过阈值就拒绝 | 实现最简单 | 临界点突刺,无法平滑处理 | 不推荐线上使用 |
| 滑动窗口 | 把时间窗口切分成小格子,滑动统计 | 比计数器更平滑 | 粒度越细,内存消耗越大 | 精度不高的限流场景 |
| 令牌桶 | 一定速率往桶里放令牌,请求消耗令牌 | 允许一定的突发流量 | 需要合理配置参数 | 大多数业务场景 |
| 漏桶 | 请求先进入桶内,以固定速率流出 | 流出速率恒定,保护下游 | 不能应对突发 | 保护数据库、消息队列 |
Gateway默认集成了基于令牌桶的限流器 RequestRateLimiter,配合Redis使用非常方便。配置刚才在路由部分已经展示过了,我再补充一个自定义KeyResolver:
@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
// 从Header中获取用户ID,如果没有则取客户端IP
String userId = exchange.getRequest().getHeaders()
.getFirst("X-User-Id");
if (StringUtils.isBlank(userId)) {
userId = exchange.getRequest().getRemoteAddress()
.getAddress().getHostAddress();
}
return Mono.just(userId);
};
}
为什么用Redis配合令牌桶?因为限流状态需要分布式共享。如果网关多实例部署,每个实例各自限流,那总流量就等于实例数乘以单机限流阈值,完全失控了。
再来一张图,把网关定位看清楚
光说文字可能有点干。我画一张图,把API网关在微服务架构中的位置和作用说清楚。
这张图展示了典型的网关部署模型。客户端只认网关这一个地址,后面有多少服务、服务内部怎么调用,客户端完全不需要知道。网关把横切关注点都处理完之后,再精准地把请求路由到对应服务。这就是解耦的魅力。
小结一下核心要点
API网关这块,面试官最常问的就是这几个问题:
- 为什么需要网关? 统一入口、协议转换、集中管控、灰度发布。一句话:不要在架构中裸奔。
- Zuul和Gateway怎么选? 新项目无脑Gateway,老项目如果还在用Zuul 1.x,尽快迁移。
- 路由和过滤怎么配置? 路由用YAML或DSL配置,过滤分局部和全局,全局需实现GlobalFilter接口。
- 限流怎么做? 推荐令牌桶+Redis分布式限流,参数设置要基于压测数据,别拍脑袋。
我一直在强调一个观点:网关不是一个BFF(后端给前端用)那么简单。它是整个系统的第一道防线,也是架构中的核心枢纽。把网关设计好,后面服务的容错压力会小很多。面试的时候,只要你把这些点讲得透彻,再加一两个亲身经历的坑,考官一定会对你印象深刻。