前言
在微服务架构中,前端直接调用众多微服务会面临 IP / 端口硬编码、无法负载均衡、权限无法统一控制 等问题。Spring Cloud Gateway 作为 Spring 官方推出的网关组件,性能远超 Zuul,是微服务生态的统一入口,承担路由转发、权限校验、流量控制、日志监控等核心职责。
一、Gateway 核心介绍
1.1 为什么要用 Gateway?
前端(Vue / 小程序 / App)直接调用微服务存在致命问题:
- 客户端维护大量 IP / 端口,硬编码严重
- 无法实现负载均衡
- 权限、日志、限流无法统一处理
- 跨域、安全难以统一管控
引入网关后架构:
前端 → Gateway(统一入口) → 微服务集群
1.2 什么是 Spring Cloud Gateway?
Spring Cloud Gateway 是 Spring 官方基于 WebFlux(Reactor+NIO) 开发的新一代网关,用于替代 Zuul,核心功能:
- 路由转发
- 断言匹配
- 过滤器链
- 限流 / 安全 / 监控
性能对比:Gateway RPS 是 Zuul 1.x 的 1.6 倍。
1.3 核心依赖注意
XML
<!-- 网关核心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
⚠️ 严禁引入:spring-boot-starter-webGateway 基于 WebFlux(非阻塞),Web 依赖是 Servlet(阻塞),会冲突导致启动失败。
二、Gateway 工程搭建
2.1 pom.xml
XML
<dependencies>
<!-- Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Gateway 网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
2.2 application.yml
XML
server:
port: 9527
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.61.132:8848 # Nacos 地址
2.3 启动类
java
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现
public class ApiGatewayApp {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApp.class, args);
}
}
三、路由配置(核心)
路由 = ID + URI + Predicates + Filters
3.1 方式一:固定地址路由(硬编码)
java
spring:
cloud:
gateway:
routes:
- id: sentinel-consumer # 唯一标识
uri: http://127.0.0.1:8080 # 固定地址
predicates:
- Path=/consumer/** # 路径匹配
3.2 方式二:服务名负载均衡(推荐)
lb:// 表示从 Nacos 获取服务列表,自动负载均衡
java
spring:
cloud:
gateway:
routes:
- id: sentinel-consumer
uri: lb://sentinel-consumer # 服务名
predicates:
- Path=/consumer/**
四、断言工厂 Predicates
断言:满足条件才路由,可组合使用。
4.1 常用内置断言工厂
| 断言 | 说明 |
|---|---|
| Path=/xxx/** | 路径匹配 |
| Method=GET | 请求方法匹配 |
| After = 时间 | 在指定时间之后 |
| Before = 时间 | 在指定时间之前 |
| Between = 时间,时间 | 时间区间 |
| RemoteAddr=IP 段 | IP 白名单 |
| Header=name,\d+ | 请求头匹配 |
| Cookie=name,xxx | Cookie 匹配 |
| Query=name,xxx | 请求参数匹配 |
示例:
java
predicates:
- Path=/consumer/**
- Method=GET
- After=2024-12-31T23:59:59.999+08:00[Asia/Shanghai]
4.2 自定义断言工厂(实战:年龄判断)
4.2.1 编写断言工厂
java
@Component
public class AgeRoutePredicateFactory
extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {
public AgeRoutePredicateFactory() {
super(Config.class);
}
// 配置文件参数顺序
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("minAge", "maxAge");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String ageStr = exchange.getRequest().getQueryParams().getFirst("age");
if (StringUtils.isNotBlank(ageStr)) {
int age = Integer.parseInt(ageStr);
return age > config.minAge && age < config.maxAge;
}
return false;
};
}
@Data
public static class Config {
private int minAge;
private int maxAge;
}
}
4.2.2 使用自定义断言
java
predicates:
- Path=/consumer/**
- Age=18,60 # 年龄在 18~60 之间才放行
五、过滤器 Filter
过滤器用于修改请求 / 响应,分为:
- 局部过滤器(单个路由)
- 全局过滤器(所有路由)
5.1 常用内置过滤器
| 过滤器 | 作用 |
|---|---|
| StripPrefix=1 | 去掉路径前缀 |
| AddRequestHeader | 添加请求头 |
| AddResponseHeader | 添加响应头 |
| RewritePath | 重写路径 |
| RequestRateLimiter | 限流 |
示例:去掉前缀
java
filters:
- StripPrefix=1 # 去掉第一层路径
5.2 自定义过滤器工厂(实战:日志耗时)
5.2.1 编写过滤器工厂
java
@Component
public class LogGatewayFilterFactory
extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {
public LogGatewayFilterFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("enable");
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
if (!config.enable) {
return chain.filter(exchange);
}
long start = System.currentTimeMillis();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
long time = System.currentTimeMillis() - start;
System.out.println("接口耗时:" + time + "ms");
}));
};
}
@Data
public static class Config {
private boolean enable;
}
}
5.2.2 使用自定义过滤器
java
filters:
- StripPrefix=1
- Log=true # 开启耗时日志
六、全局过滤器 GlobalFilter(统一鉴权)
全局过滤器对所有路由生效,常用于:登录校验、黑名单、日志。
6.1 编写登录全局过滤器
java
@Component
public class LoginFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取 token
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isBlank(token)) {
ServerHttpResponse response = exchange.getResponse();
Map<String, Object> map = new HashMap<>();
map.put("code", 401);
map.put("msg", "请先登录");
byte[] bytes = JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bytes);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeWith(Mono.just(buffer));
}
return chain.filter(exchange);
}
// 优先级,数字越小越先执行
@Override
public int getOrder() {
return 0;
}
}
6.2 测试
- 无 token:返回 401 未登录
- 带 token:正常路由转发
七、常见问题总结
- Gateway 不能引入 spring-boot-starter-web
- 服务名路由必须加 lb://
- 路径匹配尽量使用 /
- 过滤器顺序通过 getOrder () 控制
- 自定义断言 / 过滤器工厂类名必须以 XxxRoutePredicateFactory / XxxGatewayFilterFactory 结尾
八、本文总结
- Gateway 是微服务统一入口,解决硬编码、无负载均衡、无统一管控问题
- 路由支持 固定地址 与 服务名负载均衡
- 断言用于路由条件匹配
- 过滤器用于请求 / 响应增强
- 全局过滤器实现统一登录鉴权