Skip to content

网关

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

开发建议

  1. 网关只做通用逻辑,不写复杂业务。
  2. 鉴权、限流、日志要放在前置过滤器中。
  3. 网关超时要和后端服务超时配合,避免过早或过晚失败。
  4. 网关必须做监控,因为它是外部流量入口。
  5. 大文件上传、长连接等特殊流量要单独评估,不要默认都走同一套网关规则。

新项目中更常见的是 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