Ⅰ. 网关介绍
一、问题
前面学习中通过 Eureka、Nacos 解决了服务注册、服务发现的问题,使用 Spring Cloud LoadBalance 解决了负载均衡的问题,使用 OpenFeign 解决了远程调用的问题。
但是当前所有微服务的接口都是直接对外暴露的,可以直接通过外部访问。为了保证对外服务的安全性,服务端实现的微服务接口通常都带有一定的权限校验机制。并且由于使用了微服务,原本一个应用的的多个模块拆分成了多个应用,我们不得不实现多次校验逻辑。当这套逻辑需要修改时,需要同时修改多个应用,这加重了开发人员的负担。
为了解决以上问题,可以使用 API 网关。
二、什么是API网关
API网关(简称网关)也是一个服务,通常是后端服务的唯一入口 。它的定义类似设计模式中的 Facade 模式(门面模式,也称外观模式)。
它就类似整个微服务架构的门面,所有的外部客户端访问,都需要经过它来进行调度和过滤。
但它比门面模式的功能要丰富很多,经典门面模式只是接口封装 ,不关心网络、路由、安全、限流;而 API 网关是一个企业级流量治理层 ,还承担了鉴权、路由、限流等基础设施能力。
| 维度 | 代码级 Facade | Spring Cloud Gateway |
|---|---|---|
| 层级 | 应用内 | 系统边界 |
| 关注点 | 业务流程 | 流量 & 安全 |
| 调用方式 | 方法调用 | HTTP / WebFlux |
| 是否有业务逻辑 | 少 | 几乎没有 |
| 是否是门面思想 | ✔ | ✔(强化版) |

网关核心功能:
-
统一入口(门面)
-
屏蔽内部服务拓扑
-
依赖收敛
-
路由
-
鉴权
-
限流
-
熔断
-
监控
-
协议转换
三、常见网关实现
1️⃣ Spring Cloud Gateway (主流首选)
定位 :官方微服务网关
技术栈 :WebFlux + Reactor(非阻塞)
核心能力
-
动态路由(基于服务发现)
-
全局 / 局部 Filter
-
鉴权、限流、熔断
-
与 Spring Cloud 生态深度集成
适用场景
-
Spring Cloud 微服务
-
需要统一鉴权、限流、监控
评价
企业级标配,99% Java 微服务项目首选
2️⃣ Zuul(Zuul 1 / Zuul 2)
定位 :早期 Netflix 网关
-
Zuul 1:阻塞式(Servlet)
-
Zuul 2:异步(但生态断层)
现状
-
已被 Spring Cloud Gateway 取代
-
新项目不推荐
面试话术
Zuul 是历史方案,Gateway 是演进方案
3️⃣ Nginx / OpenResty
定位 :反向代理 / 七层网关
核心能力
-
反向代理、负载均衡
-
SSL 终止
-
限流、黑白名单
-
OpenResty 可写 Lua 扩展逻辑
适用场景
-
网关最前置一层
-
高并发、低延迟
-
简单路由 + 防护
典型架构
Client
↓
Nginx
↓
Spring Cloud Gateway4️⃣ Envoy
定位 :云原生 / Service Mesh 网关
核心能力
-
高性能 L7 Proxy
-
gRPC / HTTP2
-
动态配置(xDS)
-
可观测性极强
适用场景
-
Kubernetes
-
Istio / Service Mesh
-
大规模微服务
评价
技术先进,但学习成本高
5️⃣ Kong
定位 :基于 Nginx 的 API Gateway
核心能力
-
插件化(鉴权、限流、日志)
-
REST / gRPC
-
多语言支持
适用场景
-
多语言微服务
-
对外开放 API 平台
6️⃣ Traefik
定位 :云原生 Ingress / 网关
核心能力
-
自动发现服务
-
Kubernetes 原生友好
-
配置简单
适用场景
-
K8s 小中型集群
-
DevOps 驱动团队
7️⃣ 云厂商网关
-
阿里云 API 网关
-
腾讯云 API 网关
-
AWS API Gateway
适用场景
-
不想自建
-
强依赖云生态
Ⅱ. Spring Cloud Gateway
一、快速上手
① 创建网关项目
API 网关也是一个服务,所以这里单独创建一个 gateway 模块:

② 引入网关依赖
在 gateway 模块中引入网关以及相关依赖:
<!--网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--基于nacos实现服务发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>③ 编写启动类
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}④ 添加Gateway的路由配置
创建 application.yml 文件,添加如下配置:
server:
port: 10020 *# 网关端口*
spring:
application:
name: gateway *# 应用名称*
* *cloud:
nacos:
discovery:
server-addr: lirendada.art:8848 *# Nacos地址*
* *gateway:
**routes:**
- id: order-service *# 路由id,随便起个名字,不重复即可*
* *uri: lb://order-service *# 目标服务地址*
* *predicates: *# 路由条件,这里配置路径匹配,匹配到/order/**、/feign/**的请求都转发到order-service服务*
* *- Path=/order/**,/feign/**
- id: product-service
uri: lb://product-service
predicates:
- Path=/product/**配置字段说明:
-
id:自定义路由ID,保持唯一即可 -
uri:目标服务地址,支持普通 URI 及lb://应用注册服务名称。lb表示负载均衡,使用lb://方式表示从注册中心获取服务地址。 -
predicates:路由条件,根据匹配结果决定是否执行该请求路由,上述代码中,我们把符合 Path 规则的一切请求,都代理到 uri 参数指定的地址。
⑤ 测试
访问 http://127.0.0.1:10020/product/1001 和 http://127.0.0.1:10020/order/1
二、Route Predicate Factories(路由断言工厂)
① Predicate
Predicate 是 Java8 提供的一个函数式编程接口,它接收一个参数并返回一个布尔值,用于条件过滤、请求参数的校验 。其接口声明如下所示:
@FunctionalInterface
public interface Predicate<T> {
* *boolean test(T t);
* *default Predicate<T> and(Predicate<? super T> other) {
Objects.*requireNonNull*(other);
return (t) -> test(t) && other.test(t);
}
* *default Predicate<T> negate() {
return (t) -> !test(t);
}
* *default Predicate<T> or(Predicate<? super T> other) {
Objects.*requireNonNull*(other);
return (t) -> test(t) || other.test(t);
}
* *static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::*isNull*
* *: object -> targetRef.equals(object);
}
* *@SuppressWarnings("unchecked")
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.*requireNonNull*(target);
return (Predicate<T>)target.negate();
}
}代码演示:
-
定义一个
Predicate:class StringPredicate implements Predicate<String> { @Override public boolean test(String str) { return str.isEmpty(); } } -
使用这个
Predicate:public void t1() { Predicate p = new StringPredicate(); System.*out*.println(p.test("hello")); System.*out*.println(p.test("")); } // 运行结果: false true -
Predicate的其它写法:*/*** * * 匿名内部类写法* * */* @Test public void t2() { Predicate<String> p = new Predicate<String>() { @Override public boolean test(String s) { return s.length() > 0; } }; System.*out*.println(p.test("hello")); *// true* * *System.*out*.println(p.test("")); *// false* } */*** * * lambda表达式写法* * */* @Test public void t3() { Predicate<String> p = s -> s.length() > 0; System.*out*.println(p.test("hello")); *// true* * *System.*out*.println(p.test("")); *// false* }
Predicate 的其它方法:
-
isEqual(Object targetRef):比较两个对象是否相等,参数可以为 Null -
and(Predicate other):短路与操作,返回一个组成 Predicate -
or(Predicate other):短路或操作,返回一个组成 Predicate -
test(T t):传入一个 Predicate 参数,用来做判断 -
negate():返回表示此 Predicate 逻辑否定的 Predicate
| 方法 | 说明 |
|---|---|
| Predicate |
条件A&&条件B 当前Predicate的test方法 && other的test方法,相当于进行两次判断 |
| default Predicate |
!条件A 对当前判断取非操作 |
| default Predicate |
条件A||条件B 当前Predicate的test方法 || other的test方法 |
***/**** *
*** * 非操作** *
*** */** *
@Test
public void t4() {
Predicate<String> p = s -> s.length() > 0;
System.*out*.println(p.negate().test("hello")); *// false*
* *System.*out*.println(p.negate().test("")); *// true*
}
***/**** *
*** * and操作** *
*** */** *
@Test
public void t5() {
Predicate<String> p1 = s -> s.length() > 0;
Predicate<String> p2 = s -> s.startsWith("l");
System.*out*.println(p1.and(p2).test("hello")); *// false*
* *System.*out*.println(p1.and(p2).test("liren")); *// true*
}
***/**** *
*** * or操作** *
*** */** *
@Test
public void t6() {
Predicate<String> p1 = s -> s.length() > 0;
Predicate<String> p2 = s -> s.startsWith("l");
System.*out*.println(p1.or(p2).test("hello")); *// true*
* *System.*out*.println(p1.or(p2).test("liren")); *// true*
* *System.*out*.println(p1.or(p2).test("")); *// false*
}② Route Predicate Factories
在 Spring Cloud Gateway 中,Predicate 提供了路由规则的匹配机制:
spring:
cloud:
gateway:
routes: # 网关路由配置
- id: product-service
uri: lb://product-service
**predicates** :
- Path=/product/**我们在配置文件中写的断言规则只是字符串,这些字符串会被 Route Predicate Factory 读取并处理,转变为路由判断的条件。
比如例子中配置的 Path=/product/**,就是通过 Path 属性来匹配 URL 前缀是 /product 的请求。这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 实现的。
SpringCloudGateway 默认提供了很多 RoutePredicateFactory,这些 Predicate 会分别匹配 HTTP 请求的不同属性,并且多个 Predicate 可以通过 and 逻辑进行组合。
| 断言 | 作用 |
|---|---|
| Path | 路径匹配 |
| Method | HTTP 方法 |
| Header | 请求头 |
| Query | 请求参数 |
| Cookie | Cookie |
| Host | 域名 |
| After | 指定时间后 |
| Before | 指定时间前 |
| Between | 时间区间 |
| RemoteAddr | 客户端 IP |
| Weight | 权重 |
代码演示
-
在
application.yml中添加Predicate规则,新增一个After条件:spring: cloud: gateway: routes: # 网关路由配置 - id: product-service uri: lb://product-service predicates: - Path=/product/** - After=2025-12-13T14:08:33.643172900+08:00[Asia/Shanghai] # 请求时间为2025年12月13日之后 -
测试:访问 http://127.0.0.1:10020/product/1001,然后修改时间为 2026-01-01,再次访问
三、Gateway Filter Factories(网关过滤器工厂)
Predicate 决定了请求由哪一个路由处理。如果在请求处理前后需要加一些逻辑,这就是 Filter(过滤器)的作用范围了。
-
Predicate:决定 "进不进这个路由" -
Filter:决定 "进来后怎么处理"
Request
↓
RoutePredicate(匹配路由)
↓
GatewayFilter(改请求 / 鉴权 / 限流)
↓
转发到下游服务Filter分为两种类型:
-
Pre类型过滤器 :路由处理之前执行(请求转发到后端服务之前执行),在 Pre 类型过滤器中可以做鉴权、限流等。 -
Post类型过滤器 :请求执行完成后,将结果返回给客户端之前执行。
Spring Cloud Gateway 中内置了很多 Filter,用于拦截和链式处理web请求。比如权限校验、访问超时等设定。从作用范围上,Filter 可分为两种:
-
GatewayFilter:应用到单个路由或者一个分组的路由上。 -
GlobalFilter:应用到所有的路由上,即对所有的请求生效。
① GatewayFilter
GatewayFilter 与 Predicate 类似,都是在配置文件 application.yml 中配置,每个过滤器的逻辑都是固定的。
比如 AddRequestParameterGatewayFilterFactory 只需要在配置文件中写 AddRequestParameter,就可以为所有的请求添加一个参数,我们先通过一个例子来演示 GatewayFilter 如何使用。
快速上手
- 在
application.yml中添加filter:server: port: 10020 *# 网关端口* spring: application: name: gateway *# 应用名称* * *cloud: nacos: discovery: server-addr: lirendada.art:8848 *# Nacos地址* * *gateway: routes: - id: order-service * *uri: lb://order-service * *predicates: * *- Path=/order/**,/feign/** **filters** : - addRequestParameter=userName, lirendada # 每个请求都带上键值对{userName, lirendada} - id: product-service uri: lb://product-service predicates: - Path=/product/** - After=2026-12-13T14:08:33.643172900+08:00[Asia/Shanghai]
该 filter 只添加在了 order-service 路由下,因此只对 order-service 路由生效。
-
接收参数并打印:在
order-service服务中接收请求的参数,并打印出来:@Slf4j @RestController @RequestMapping("/feign") public class FeginController { @Autowired private ProductApi productApi; @RequestMapping("/t1") public String test1(String userName) { *log*.info("接收到filter请求添加的参数:userName={}", userName); return productApi.p1(1); } } -
测试:访问 http://127.0.0.1:8080/feign/t1 以及 http://127.0.0.1:10020/feign/t1 观察区别:

说明
Spring Cloud Gateway 提供了的 Filter 非常多,下面列出一些常见过滤器的说明。
详细可参考官方文档:GatewayFilterFactories
default-filters
前面提到的 filter 都是添加在指定路由下的,所以只对当前路由生效。若需要对全部路由生效,可以使用 spring.cloud.gateway.default-filters,这个属性需要一个 filter 的列表。
举个例子:
spring:
application:
name: gateway *# 应用名称*
* *cloud:
nacos:
discovery:
server-addr: lirendada.art:8848
* *gateway:
routes:
- id: order-service
* *uri: lb://order-service
* *predicates:
* *- Path=/order/**,/feign/**
filters:
- AddRequestParameter=userName, lirendada
- id: product-service
uri: lb://product-service
predicates:
- Path=/product/**
- After=2026-12-13T14:08:33.643172900+08:00[Asia/Shanghai]
**default-filters: # 和routes是同级的**
- name: Retry
args:
retries: 3
series: BAD_GATEWAY然后修改一下之前 controller 的代码:
@RequestMapping("/t1")
public String test1(String userName, HttpServletResponse response) {
*log*.info("接收到filter请求添加的参数:userName={}", userName);
response.setStatus(502);
return productApi.p1(1);
}访问 http://127.0.0.1:10020/feign/t1 观察:

③ GlobalFilter
GlobalFilter 是 Spring Cloud Gateway 中的全局过滤器,它和 GatewayFilter 的作用是相同的,但和 default-filters 有以下区别:
| 维度 | default-filters | GlobalFilter |
|---|---|---|
| 配置方式 | YAML | Java |
| 生效范围 | 所有路由 | 所有路由 |
| 灵活性 | 低 | 极高 |
| 是否写代码 | 否 | 是 |
| 是否可访问业务资源 | 否 | 是 |
| 顺序控制 | 固定(内部) | 可精确控制 |
| 可维护性 | 高 | 依赖实现质量 |
Spring Cloud Gateway 内置的全局过滤器也有很多,比如:
-
The Gateway Metrics Filter:网关指标,提供监控指标 -
Forward Routing Filter:用于本地 forword,请求不转发到下游服务器 -
ReactiveLoadBalancerClientFilter:针对下游服务,实现负载均衡 -
……
更多过滤器参考:GlobalFilters
快速上手(这里以 The Gateway Metrics Filter为例)
-
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> -
添加配置
spring: cloud: gateway: metrics: enabled: true management: endpoints: web: exposure: include: "*" endpoint: health: show-details: always shutdown: enabled: true

四、过滤器执行顺序
一个项目中,既有 GatewayFilter,又有 GlobalFilter 时,执行的先后顺序是什么呢?
请求路由后,网关会把当前项目中的 GatewayFilter 和 GlobalFilter 合并到一个过滤器链中,并进行排序,依次执行过滤器。

每一个过滤器都必须指定一个 int 类型的 order 值,默认值为 0,表示该过滤的优先级。order 值越小,优先级越高,执行顺序越靠前 。
-
Filter 通过实现
Order接口 或者添加@Order注解 来指定 order 值。 -
Spring Cloud Gateway 提供的 Filter 由 Spring 指定,当然用户也可以自定义 Filter,由用户指定。
-
当过滤器的 order 值一样时,会按照
defaultFilter>GatewayFilter>GlobalFilter的顺序执行。
五、自定义过滤器
Spring Cloud Gateway 提供了过滤器的扩展功能,开发者可以根据实际业务来自定义过滤器,同样自定义过滤器也支持 GatewayFilter 和 GlobalFilter 两种。
① 自定义GatewayFilter
自定义 GatewayFilter,需要去实现接口 GatewayFilterFactory,不过 SpringBoot 默认帮我们实现了一个抽象类 AbstractGatewayFilterFactory,所以直接继承该抽象类即可。
1. 创建 GatewayFilterFactory(核心)
@Slf4j
@Component
public class CustomGatewayFilterFactory **extends AbstractGatewayFilterFactory** <CustomGatewayFilterFactory.CustomConfig> **implements Ordered** {
*/***
* * 告诉 Gateway:这个 GatewayFilterFactory 使用哪一种配置对象,如何把 YAML 中的参数绑定进来。*
* */*
* *public CustomGatewayFilterFactory() {
super(CustomConfig.class);
}
@Override
public GatewayFilter apply(CustomConfig config) {
*/***
* * Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)*
* * ServerWebExchange: HTTP请求-响应交互的契约, 提供对HTTP请求和响应的访问, 服务器端请求属性, 请求实例,响应实例等, 类似Context角色*
* * GatewayFilterChain: 过滤器链*
* * Mono: Reactor核心类, 数据流发布者, Mono最多只触发一个事件, 所以可以把Mono 用于在异步任务完成时发出通知.*
* * Mono.fromRunnable: 创建一个包含Runnable元素的数据流*
* */*
* *return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
*log*.info("[Pre] Custom Gateway Filter"); *// 自定义的 GatewayFilter 逻辑*
* *return chain.filter(exchange).then(Mono.*fromRunnable*(() -> {
*log*.info("[Post] Custom Gateway Filter");
}));
}
};
}
@Override
public int getOrder() {
return Ordered.*LOWEST_PRECEDENCE*; *// 配置优先级, order越大, 优先级越低*
* *}
@Data
public static class CustomConfig {
private String name;
}
}-
自定义过滤器的类名统一以
GatewayFilterFactory结尾 。因为默认情况下,过滤器的 name 会采用该定义类的前缀,这里的 name=Custom。 -
apply()方法中,可以同时包含 Pre 和 Post 过滤,并且then()方法是请求执行结束之后处理的。 -
CustomConfig是一个配置类,该类我们设定只有一个属性 name,会和 yml 的配置对应。 -
该类需要交给 Spring 管理,所以需要加五大注解。
-
getOrder表示该过滤器的优先级,值越大,优先级越低。
2. 配置过滤器
spring:
application:
name: gateway
* *cloud:
nacos:
discovery:
server-addr: lirendada.art:8848
* *gateway:
routes:
- id: order-service
* *uri: lb://order-service
* *predicates:
* *- Path=/order/**,/feign/**
filters:
- AddRequestParameter=userName, lirendada
**- name: Custom**
**args:**
**name: lirendada # 这里的 name 是 CustomConfig 的字段,要和上面区分开** 3. 测试
重启服务,访问接口,观察日志:http://127.0.0.1:10020/order/1

② 自定义GlobalFilter
自定义 GlobalFilter 比较简单,它不需要额外的配置,因为它全局生效,所以只需要实现 GlobalFilter接口 ,自动会过滤所有的 Filter。
1. 定义GlobalFilter
@Slf4j
@Component
public class CustomGlobalFilterFactory **implements GlobalFilter** , **Ordered** {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
*log*.info("[Pre] Custom GlobalFilter Filter"); *// 自定义的 GlobalFilter 逻辑*
* *return chain.filter(exchange).then(Mono.*fromRunnable*(() -> {
*log*.info("[Post] Custom GlobalFilter Filter");
}));
}
@Override
public int getOrder() {
return Ordered.*LOWEST_PRECEDENCE*;
}
}2. 测试
重启服务,访问接口,观察日志:http://127.0.0.1:10020/order/1

从日志中可以看出来,当 GatewayFilter 和 GlobalFilter 过滤器的 order 值一样时,会先执行 GatewayFilter。
Ⅲ. 服务部署
-
修改数据库,Nacos 等相关配置
-
对三个服务进行打包:product-service、order-service、gateway
-
上传 jar 包到 Linux 服务器
-
启动 Nacos
-
启动服务
#后台启动order-service, 并设置输出日志到logs/order.log
nohup java -jar order-service.jar >logs/order.log &
#后台启动product-service, 并设置输出日志到logs/order.log
nohup java -jar product-service.jar >logs/product-9090.log &
#启动网关
nohup java -jar gateway.jar >logs/gateway.log &