兄弟们,先灵魂拷问:
微服务拆了 5 个服务后,前端小姐姐是不是天天追着你要 "统一接口地址"?
每个服务都写一遍 token 验证,是不是感觉自己在 "复制粘贴式搬砖"?
线上排查问题,想拦个请求看参数,是不是得改 N 个服务的代码?
如果你中了两条以上 ------ 别慌,你缺的不是咖啡,是一个 "微服务管家":网关!
一、先搞懂:网关到底是个啥?
别被 "架构名词" 吓到,网关其实就是个 "中间商",干 3 件接地气的事:
- 看门:谁能进(鉴权)、谁不能进(拦截非法请求),比如没 token 直接打回;
- 指路:用户访问/api/user,自动转发到用户服务,不用记 N 个服务地址;
- 打杂:给请求加个 header、改个路径、记录个日志,不用每个服务自己干。
简单说:网关 = 小区保安 + 快递中转站 + 公司行政,让微服务们专心 "搞业务"。
二、网关技术方案:该 pick 谁?
市面上的网关不少,别瞎跟风,看场景选:
方案 | 特点 | 适合场景 |
---|---|---|
Kong/APISIX | 基于 Nginx,性能强,运维友好 | 多语言架构、高并发场景 |
Spring Cloud Gateway | 纯 Java,和 Spring 生态无缝衔接 | Java 后端主导的微服务 |
Zuul | 老牌网关,性能一般 | 老项目维护(新项目别用) |
咱们后端搬砖人,大多是 Spring 生态,所以重点聊 Spring Cloud Gateway(以下简称 Gateway)。

三、为啥 Gateway 把 Zuul 按在地上摩擦?
老玩家可能记得 Zuul,但现在基本被 Gateway 取代,原因很真实:
- 性能差太多:Zuul 1.x 是同步阻塞(请求来了等半天),Gateway 基于 Netty 异步非阻塞,吞吐量直接翻倍;
- 功能太拉胯:Zuul 想加个动态路由、自定义过滤,得改代码重启;Gateway 支持配置化,改 yaml 就行;
- Zuul 2.x 跳票坑:Zuul 2.x 本来想补异步的坑,结果跳票 N 年,等它出来,Gateway 已经占领半壁江山了...
总结:选 Gateway,就是选 "不加班"------ 毕竟谁也不想因为 Zuul 的性能问题,半夜起来改代码。
四、实战:10 分钟搭一个 Gateway 服务
光说不练假把式,咱们用 Spring Boot + Gateway + Nacos(注册中心)搭一个,步骤超简单:
1. 加依赖(别踩坑!)
注意:Gateway 不能加spring-boot-starter-web依赖,会冲突!
xml
<dependencies>
<!-- Gateway核心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 注册中心(Nacos为例,Eureka/Consul也一样) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
2. 写配置(application.yml)
核心是 "路由规则":用户访问啥路径,转发到哪个服务?
yaml
spring:
application:
name: gateway-service # 网关服务名
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos地址
gateway:
routes:
# 路由1:转发用户服务
- id: user-service-route # 唯一标识(随便起)
uri: lb://user-service # 转发到Nacos中的user-service(lb=负载均衡)
predicates: # 匹配规则(断言)
- Path=/api/user/** # 只要访问/api/user/开头的路径,就走这个路由
filters: # 过滤规则
- RewritePath=/api/user/(?<segment>.*), /user/${segment} # 路径重写:/api/user/1 → /user/1
# 路由2:转发订单服务(同理)
- id: order-service-route
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- RewritePath=/api/order/(?<segment>.*), /order/${segment}
3. 加启动类
就加个@EnableDiscoveryClient,没啥花活:
less
@SpringBootApplication
@EnableDiscoveryClient // 注册到Nacos
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
启动后,访问http://localhost:8080/api/user/1,会自动转发到user-service的/user/1接口 ------ 成了!
五、路由断言:网关的 "指路牌"
刚才配置里的predicates就是 "断言",简单说:满足条件的请求,才走这个路由。
常用断言整理好了,直接抄:
断言类型 | 例子 | 意思 |
---|---|---|
Path(路径) | Path=/api/user/** | 匹配 /api/user/ 开头的路径 |
After(时间) | After=2024-05-01T00:00:00+08:00[Asia/Shanghai] | 2024 年 5 月 1 日后才生效 |
Query(参数) | Query=token | 请求必须带 token 参数(如?token=xxx) |
RemoteAddr(IP) | RemoteAddr=192.168.1.0/24 | 只允许 192.168.1.x 网段的请求 |
可以多个断言组合,比如:Path=/api/user/** && Query=token------ 既匹配路径,又得带 token 参数。
六、网关过滤:给请求 "加 buff" 或 "拦坑"
过滤分两种:内置过滤器 (现成的)和自定义过滤器(自己写),都是为了处理请求。
1. 内置过滤器:拿来就用
Gateway 自带很多过滤器,不用写代码,配置就行:
过滤器名 | 例子 | 作用 |
---|---|---|
AddRequestHeader | AddRequestHeader=X-User-Id, 123 | 给请求加个 X-User-Id 头,值为 123 |
SetStatus | SetStatus=404 | 把响应状态码改成 404 |
RewritePath | 前面实战用过,路径重写 | 隐藏真实接口路径 |
RequestRateLimiter | 限流用(后续单独讲) | 防止接口被刷爆 |

如图所示:
- 客户端请求进入网关后由
HandlerMapping
对请求做判断,找到与当前请求匹配的路由规则(Route
),然后将请求交给WebHandler
去处理。 WebHandler
则会加载当前路由下需要执行的过滤器链(Filter chain
),然后按照顺序逐一执行过滤器(后面称为**Filter
**)。- 图中
Filter
被虚线分为左右两部分,是因为Filter
内部的逻辑分为pre
和post
两部分,分别会在请求路由到微服务之前 和之后被执行。 - 只有所有
Filter
的pre
逻辑都依次顺序执行通过后,请求才会被路由到微服务。 - 微服务返回结果后,再倒序执行
Filter
的post
逻辑。 - 最终把响应结果返回。
在网关过滤器链中,有两种过滤器:
GatewayFilter
:路由过滤器,作用范围比较灵活,可以是任意指定的路由Route
.GlobalFilter
:全局过滤器,作用范围是所有路由,不可配置。通常用于自定义过滤器实现拦截功能的
💡
FilteringWebHandler
在处理请求时,会将GlobalFilter
装饰为GatewayFilter
,然后放到过滤器链中,排序以后依次执行。
2. 自定义过滤器:满足特殊需求
过滤器分两类,别搞混:
(1)GatewayFilter:路由级过滤(只对某个路由生效)
比如给 "用户服务路由" 加个过滤器,打印请求日志:
typescript
// 1. 写过滤器
@Component
public class LogGatewayFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 打印请求路径
System.out.println("请求路径:" + exchange.getRequest().getPath());
// 继续往下走(不放行就卡这了)
return chain.filter(exchange);
}
// 过滤器顺序(数字越小越先执行)
@Override
public int getOrder() {
return 0;
}
}
// 2. 配置路由时引用
spring:
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- name: LogGatewayFilter # 引用自定义的过滤器
(2)GlobalFilter:全局过滤(所有请求都生效)
比如全局打印请求时间,不用在每个路由配置:
java
@Component
public class GlobalLogFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long start = System.currentTimeMillis();
// 执行完后续逻辑后,打印耗时
return chain.filter(exchange)
.doFinally(signal -> {
long end = System.currentTimeMillis();
System.out.println("请求耗时:" + (end - start) + "ms");
});
}
@Override
public int getOrder() {
return -1; // 全局过滤器,先执行
}
}
七、鉴权:网关的 "门禁卡",必须搞懂!
最核心的场景来了:所有请求必须带合法 token,否则不让进。
为啥在网关鉴权?------ 总不能每个服务都写一遍 token 验证吧?网关统一拦,效率高!
鉴权逻辑:3 步走
- 从请求头里拿 token(比如Authorization: Bearer xxx);
- 验证 token 是否合法(调用认证服务,或用 JWT 解密);
- 合法就放行,不合法返回 401(未授权)。

实战:自定义全局鉴权过滤器
用 JWT 为例(无状态,适合微服务):
java
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
// 假设这是JWT工具类(实际项目用现成的,比如jjwt)
private final JwtUtil jwtUtil;
@Autowired
public AuthGlobalFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 跳过白名单(比如登录接口,不用鉴权)
String path = exchange.getRequest().getPath().value();
if ("/api/auth/login".equals(path)) {
return chain.filter(exchange);
}
// 2. 拿token
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
return handleUnauthorized(exchange); // 没token,返回401
}
token = token.substring(7); // 去掉"Bearer "前缀
// 3. 验证token
try {
Claims claims = jwtUtil.parseToken(token); // JWT解密
// 把用户信息放入上下文,后续服务可以直接拿
exchange.getAttributes().put("userId", claims.get("userId"));
} catch (Exception e) {
return handleUnauthorized(exchange); // token无效,返回401
}
// 4. 放行
return chain.filter(exchange);
}
// 处理未授权:返回401
private Mono<Void> handleUnauthorized(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String json = "{"code":401,"msg":"未授权,请先登录"}";
DataBuffer buffer = response.bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
return -2; // 比全局日志过滤器先执行
}
}
这样一来,所有非白名单请求,都会先过鉴权 ------ 搞定!
总结:网关学好,搬砖没烦恼
- 网关是微服务的 "管家",负责路由、过滤、鉴权;
- 选 Gateway 不选 Zuul,因为性能好、生态近;
- 路由靠断言,过滤分内置 / 自定义,鉴权用全局过滤器;
- 实战代码直接抄,注意别踩 "加 web 依赖" 的坑。
最后问一句:你在网关实战中遇到过啥奇葩问题?比如动态路由不生效、鉴权丢头?评论区聊聊,一起避坑~