网关
Zuul 是微服务网关。外部请求不会直接访问每个后端服务,而是先进入网关,再由网关根据路由规则转发到具体服务。
网关的价值是把鉴权、限流、日志、跨域、灰度、路由这些横切逻辑集中处理,避免每个业务服务重复实现。
阅读入口:按过滤器生命周期追踪请求
按“pre → route → post;异常进入 error”阅读。先理解 Zuul 1 的 Servlet 阻塞模型,再看过滤器扩展和向 Gateway 迁移。
为什么微服务需要网关
如果客户端直接访问每个微服务,就必须知道所有服务地址、认证方式、限流规则和接口版本。服务一多,客户端会和后端拓扑强耦合。网关把外部入口收敛起来,让客户端只面对一个统一地址,后端服务变更由网关路由处理。
核心原理:过滤器生命周期驱动一次转发
Zuul 1 基于 Servlet 阻塞模型,通过 pre → route → post 过滤器阶段组织请求;发生异常时进入 error 阶段。路由过滤器负责真正转发,其他过滤器只处理横切逻辑,不应承载订单、支付等领域业务。
mermaid
flowchart TD
A["客户端请求"] --> B["pre过滤器:鉴权/限流/Trace"]
B --> C["RouteLocator匹配服务与路径"]
C --> D["route过滤器:Ribbon/HTTP转发"]
D --> E{"下游成功?"}
E -- "是" --> F["post过滤器:日志/响应头"]
E -- "否" --> G["error过滤器:统一错误"]
F --> H["返回客户端"]
G --> H请求流程
mermaid
flowchart TD
A["客户端"] --> B["Zuul 网关"]
B --> C["前置过滤器<br/>鉴权 / 限流 / 日志"]
C --> D["路由到目标服务"]
D --> E["后置过滤器<br/>响应处理"]
E --> F["返回客户端"]网关常见职责
| 职责 | 说明 |
|---|---|
| 统一入口 | 所有外部请求从网关进入 |
| 路由转发 | 根据路径、服务名、版本转发到后端服务 |
| 认证鉴权 | 校验 token、权限、签名 |
| 限流保护 | 防止恶意流量或突发流量压垮服务 |
| 日志审计 | 统一记录访问日志和 traceId |
| 跨域处理 | 统一处理 CORS |
| 灰度发布 | 按用户、Header、版本转发到不同实例 |
Zuul 过滤器
Zuul 的核心扩展点是过滤器。过滤器按执行阶段可以分为:
- pre:路由前执行,适合鉴权、参数校验、限流。
- route:路由阶段执行,负责请求转发。
- post:路由后执行,适合记录日志、修改响应。
- error:异常时执行,适合统一错误处理。
mermaid
flowchart TD
A[请求进入] --> B[pre 过滤器]
B --> C[route 过滤器]
C --> D[业务服务]
D --> E[post 过滤器]
B --> F[error 过滤器]
C --> F
D --> F网关和注册中心
Zuul 可以结合 Eureka 使用服务名路由。这样网关不需要写死后端服务 IP,只要知道服务名即可。
示例:
/api/order/**转发到order-service/api/user/**转发到user-service/api/pay/**转发到pay-service
开发建议
- 网关只做通用逻辑,不写复杂业务。
- 鉴权、限流、日志要放在前置过滤器中。
- 网关超时要和后端服务超时配合,避免过早或过晚失败。
- 网关必须做监控,因为它是外部流量入口。
- 大文件上传、长连接等特殊流量要单独评估,不要默认都走同一套网关规则。
新项目中更常见的是 Spring Cloud Gateway,但 Zuul 的过滤器思想和网关职责仍然值得掌握。
代码 Demo:Zuul 前置鉴权过滤器
下面示例演示请求进入后先检查 token,没有 token 就直接拒绝。
java
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class AuthFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String token = request.getHeader("token");
if (token == null || token.isBlank()) {
context.setSendZuulResponse(false);
context.setResponseStatusCode(401);
context.setResponseBody("missing token");
}
return null;
}
}路由配置示例:
yaml
zuul:
routes:
order:
path: /api/order/**
serviceId: order-service这样访问 /api/order/** 时,请求会先经过过滤器,再转发到 order-service。
