大家好呀!今天来跟大家好好聊聊微服务里超重要的 “守门人”——Spring Cloud Gateway。不管是做微服务架构设计,还是日常开发调试,网关都是绕不开的环节,这篇就从基础到实战,把 Gateway 的核心知识点讲明白,新手也能轻松看懂~
一、先搞懂:为什么需要网关?Gateway 是什么?
首先得明确一个问题:微服务集群里,为啥非要整个网关?
其实网关就像小区的保安亭,所有外部请求都得先经过它,再转发到对应的微服务。没有网关的话,每个微服务都要自己处理权限校验、流量控制,既冗余又难维护,而网关能把这些 “通用活儿” 全揽下来,让微服务专心搞业务。
再说说 Spring Cloud Gateway 本身:它是 Spring Cloud 家族的新成员,基于 Spring 5.0、Spring Boot 2.0 和响应式编程技术开发,性能比老款的 Zuul(基于 Servlet 阻塞编程)好不少,现在基本是微服务网关的首选。
二、Gateway 核心功能:这 3 件事它最擅长
网关不是花架子,核心就干 3 件关键事儿,咱们一个个说:
- 权限控制:比如用户没登录、没权限,网关直接拦住请求,不用让请求跑到微服务里再被打回来,效率高多了。
- 路由 + 负载均衡:你访问
/product-serv/xxx
,网关知道要转发到 “商品微服务”;要是商品微服务有 3 个实例,网关还能自动帮你分配请求,实现负载均衡(不用自己再搭 Nginx 那套)。 - 限流:比如秒杀活动时请求突然暴涨,网关能按微服务能承受的速度 “放行”,避免微服务被冲垮,保障系统稳定。
放个简单的架构图,大家一看就懂:
注册中心/配置中心(Nacos)
↓(微服务注册、网关读配置)
微服务A(连数据库)←→ Feign ←→ Gateway网关 ←→ Feign → 微服务B(连数据库)
↓
微服务C(连数据库)
三、实战上手:Gateway 快速搭建(超详细步骤)
光说不练假把式,咱们一步步搭建一个基础网关,实现路由功能。
3.1 第一步:创建 Gateway 服务,引入依赖
先建一个 Spring Boot 模块(比如叫api-gateway
),然后在pom.xml
里加两个关键依赖:网关本身和 Nacos 服务发现(后面负载均衡要用)。
<!-- 网关核心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos服务发现依赖(网关要从Nacos找微服务) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3.2 第二步:写配置文件,搞定路由规则
接下来写application.yml
,核心是配置 “服务基本信息” 和 “路由规则”。这里分两个版本,新手先看基础版,再看高级版。
基础版(固定地址转发)
适合刚开始测试,直接指定微服务的 IP + 端口:
yaml
server:
port: 7000 # 网关的端口
spring:
application:
name: api-gateway # 网关服务名
cloud:
gateway:
routes: # 路由数组:一个路由对应一个微服务的转发规则
- id: product_route # 路由唯一标识(随便起,但不能重复)
uri: http://localhost:8081/ # 转发目标地址(商品微服务的地址)
order: 1 # 优先级:数字越小越先执行
predicates: # 断言:满足这个条件才转发
- Path=/product-serv/** # 路径规则:请求路径以/product-serv开头就转发
filters: # 过滤器:转发前处理请求
- StripPrefix=1 # 去掉路径的第一级(比如请求/product-serv/product/19,转发后变成/product/19)
高级版(从 Nacos 拉取微服务,支持负载均衡)
实际项目里微服务地址会变,而且可能多实例,这时候就用lb://服务名
的方式,让网关从 Nacos 找微服务,自动做负载均衡:
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos的地址
gateway:
discovery:
locator:
enabled: true # 允许网关发现Nacos里的微服务
routes:
- id: product_route
uri: lb://service-product # lb=负载均衡,service-product是微服务在Nacos的名字
order: 1
predicates:
- Path=/product-serv/**
filters:
- StripPrefix=1
还要注意:启动类上要加@EnableDiscoveryClient
注解,让网关能注册到 Nacos~
3.3 第三步:测试一下
启动网关、Nacos 和商品微服务(假设商品微服务端口 8081,有个接口/product/19
):
访问http://localhost:7000/product-serv/product/19
,网关会自动转发到http://localhost:8081/product/19
,能拿到结果就说明搭建成功啦!
四、断言工厂:网关怎么判断 “该转发给谁”?
前面配置里的predicates: - Path=/product-serv/**
,其实是用了 “路径断言”,背后是 Spring 提供的 “断言工厂” 在工作。简单说:咱们写的断言字符串,会被断言工厂转换成判断逻辑,只有满足逻辑的请求才会被转发。
Spring Cloud Gateway 提供了十几种断言工厂,常用的列个表,大家按需选:
断言工厂(对应配置关键词) | 作用 | 示例 |
---|---|---|
After | 只转发 “某个时间点之后” 的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | 只转发 “某个时间点之前” 的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | 转发 “两个时间点之间” 的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver],2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含指定 Cookie | - Cookie=chocolate, ch.p(Cookie 名 chocolate,值含 ch.p) |
Header | 请求必须包含指定 Header | - Header=X-Request-Id, \d+(Header 名 X-Request-Id,值是数字) |
Host | 请求必须访问指定域名 | - Host=.somehost.org,.anotherhost.org |
Method | 只转发指定请求方式(GET/POST 等) | - Method=GET,POST |
Path | 路径匹配(最常用) | - Path=/red/{segment},/blue/** |
Query | 请求必须包含指定参数 | - Query=name(必须有 name 参数);- Query=name,Jack(name 参数值是 Jack) |
RemoteAddr | 请求 IP 在指定范围 | - RemoteAddr=192.168.1.1/24 |
五、过滤器工厂:请求转发前后能做啥?
网关除了转发请求,还能在转发前 / 后修改请求 / 响应,这就靠 “过滤器工厂”。比如加个请求头、删个响应头,或者做限流,都能用现成的过滤器工厂。
5.1 常用过滤器工厂
Spring 提供了 31 种过滤器工厂,挑几个常用的说说:
过滤器工厂(配置关键词) | 作用 |
---|---|
AddRequestHeader | 给请求加一个 Header |
RemoveRequestHeader | 从请求里删一个 Header |
AddResponseHeader | 给响应加一个 Header |
RemoveResponseHeader | 从响应里删一个 Header |
RequestRateLimiter | 对请求限流(防止接口被刷爆) |
5.2 实战:给请求加个 Header
比如想让所有发往商品微服务的请求,都带上msg: wake up!
的请求头,修改application.yml
的filters
即可:
routes:
- id: product_route
uri: lb://service-product
order: 1
predicates:
- Path=/product-serv/**
filters:
- StripPrefix=1
- AddRequestHeader=msg, wake up! # 加请求头
5.3 默认过滤器:对所有路由生效
如果想让某个过滤器对所有路由都生效(比如所有请求都加同一个 Header),不用每个路由都写一遍,用default-filters
配置即可:
spring:
cloud:
gateway:
default-filters: # 默认过滤器,所有路由都生效
- AddRequestHeader=msg, wake up!
routes:
- id: product_route
# 其他配置不变...
六、全局过滤器:自定义业务逻辑(比如权限校验)
前面的过滤器都是 “现成的”,只能干固定的活儿。但实际项目里,经常需要自定义逻辑(比如判断用户是否登录、有没有权限),这时候就需要 “全局过滤器”—— 自己写代码实现,想干啥就干啥。
6.1 全局过滤器 vs 普通过滤器
- 普通过滤器(GatewayFilter):靠配置生效,逻辑固定,只能针对特定路由。
- 全局过滤器(GlobalFilter):自己写代码,对所有路由生效,逻辑灵活(比如登录校验、权限判断)。
6.2 实战:自定义全局过滤器(权限校验)
需求:所有请求必须带authorization
参数,且值为admin
,否则拦截(返回 401 未授权)。
步骤 1:写过滤器类
实现GlobalFilter
接口,加@Component
让 Spring 扫描到,再用@Order
指定优先级(数字越小越先执行):
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
@Order(-1) // 优先级:-1比默认的高,先执行
public class AuthorizeFilter implements GlobalFilter {
// 核心逻辑:处理请求,决定放行还是拦截
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取请求参数(从URL里拿)
var params = exchange.getRequest().getQueryParams();
// 2. 拿到authorization参数的值
String auth = params.getFirst("authorization");
// 3. 校验:如果参数是admin,就放行
if ("admin".equals(auth)) {
// 放行:把请求交给下一个过滤器
return chain.filter(exchange);
}
// 4. 拦截:返回401未授权
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 结束请求处理
return exchange.getResponse().setComplete();
}
}
步骤 2:测试
- 访问
http://localhost:7000/product-serv/product/19?authorization=admin
:正常放行,返回结果。 - 访问
http://localhost:7000/product-serv/product/19
(没带参数),或参数值不是 admin:返回 401,被拦截。
6.3 过滤器执行顺序
请求进入网关后,会碰到 3 类过滤器:默认过滤器(default-filters)、路由过滤器(routes 里的 filters)、全局过滤器(GlobalFilter)。它们的执行顺序按以下规则:
- 所有过滤器都有
order
值,order
越小,越先执行。 - 全局过滤器的
order
自己指定(@Order 注解)。 - 默认过滤器和路由过滤器的
order
由 Spring 自动分配,按配置顺序从 1 开始递增。 - 若
order
相同:默认过滤器 > 路由过滤器 > 全局过滤器。
七、跨域问题:网关怎么解决?
做前后端分离项目时,肯定会碰到 “跨域”—— 浏览器出于安全考虑,禁止前端访问不同域名 / 端口的接口。比如前端跑在localhost:8090
,网关跑在localhost:7000
,直接请求就会被浏览器拦截。
7.1 先懂同源策略
浏览器的 “同源” 指 3 个相同:协议相同(比如都是 http)、域名相同(比如都是www.xxx.com)、端口相同(比如都是 80)。只要有一个不同,就是跨域。
7.2 网关解决跨域:配置 CORS
不用在每个微服务里配跨域,网关统一配就行。修改application.yml
,加全局跨域配置:
spring:
cloud:
gateway:
globalcors: # 全局跨域配置
add-to-simple-url-handler-mapping: true # 解决OPTIONS请求被拦截问题
corsConfigurations:
'[/**]': # 对所有路径生效
allowedOrigins: # 允许哪些前端域名访问
- "http://localhost:8090"
allowedMethods: # 允许的请求方式
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allowedHeaders: "*" # 允许前端带任何Header
allowCredentials: true # 允许前端带Cookie
maxAge: 360000 # 跨域校验结果的有效期(1小时,避免频繁校验)
配完重启网关,前端再请求就不会有跨域问题了~
总结
本篇把 Spring Cloud Gateway 的核心知识点都覆盖了:从 “为什么需要网关”,到搭建、路由、断言、过滤器、全局逻辑、跨域解决,每个点都带了实战代码,新手跟着做就能上手。实际项目里,网关还能结合限流(比如用 Redis 实现)、日志收集等功能,后续有机会再跟大家深入聊~
如果有疑问,欢迎评论区交流呀!