微服务网关设计与实战:Spring Cloud Gateway 全解析
微服务网关作为微服务架构的核心入口,负责统一路由转发、安全防护及流量控制。本文基于 Spring Cloud Gateway 详解其核心架构(路由、断言、过滤器),涵盖静态与动态路由配置、JWT 统一认证、全局日志收集、跨域处理等实战功能。同时整合 Sentinel 实现限流熔断,并探讨高可用部署方案与性能优化策略,帮助开发者构建稳定高效的微服务网关系统。

微服务网关作为微服务架构的核心入口,负责统一路由转发、安全防护及流量控制。本文基于 Spring Cloud Gateway 详解其核心架构(路由、断言、过滤器),涵盖静态与动态路由配置、JWT 统一认证、全局日志收集、跨域处理等实战功能。同时整合 Sentinel 实现限流熔断,并探讨高可用部署方案与性能优化策略,帮助开发者构建稳定高效的微服务网关系统。

在微服务架构中,客户端(Web/APP)需要与多个微服务直接通信,会面临接口分散、认证复杂、跨域问题、流量控制困难等挑战。微服务网关作为整个微服务架构的'入口',统一接收客户端请求,再转发到对应的微服务,相当于'流量调度中心'和'安全防护墙'。
微服务网关的核心价值体现在以下 6 个方面:
/api/user/*、/api/order/* 等统一路径访问不同服务,无需关心用户服务、订单服务的具体部署地址。目前 Java 生态中主流的微服务网关有 3 种:Spring Cloud Gateway、Zuul、Kong,各有优劣,需根据业务场景选型:
| 技术选型 | 核心优势 | 不足之处 | 适用场景 |
|---|---|---|---|
| Spring Cloud Gateway | 1. 基于 Spring 生态,与 Spring Cloud 无缝整合; 2. 基于 Netty 异步非阻塞,性能优异(吞吐量是 Zuul 的 2-3 倍); 3. 支持动态路由、灵活的断言与过滤器; 4. 原生支持 WebSocket、限流、熔断等功能 | 1. 仅支持 Spring Boot/Spring Cloud 项目; 2. 学习成本略高于 Zuul | Spring Cloud 生态的微服务项目,对性能要求较高的场景 |
| Zuul(1.x/2.x) | 1. 简单易用,与 Spring Cloud 整合成熟; 2. 社区成熟,文档丰富; 3. Zuul 2.x 支持异步非阻塞 | 1. Zuul 1.x 基于 Servlet 同步阻塞,性能较差; 2. 功能扩展性不如 Spring Cloud Gateway | 小型微服务项目,对性能要求不高,追求开发效率 |
| Kong | 1. 基于 Nginx,性能极高,支持高并发; 2. 支持多语言微服务(不局限于 Java); 3. 丰富的插件生态(认证、限流、监控等) | 1. 与 Spring Cloud 生态整合不够紧密; 2. 配置复杂,二次开发成本高 | 多语言混合开发的微服务项目,对性能和并发要求极高的场景 |
推荐选型:Spring Cloud Gateway 是目前 Spring Cloud 生态的首选网关,兼顾性能、易用性与扩展性,本章重点讲解其设计与实战。
Spring Cloud Gateway 基于异步非阻塞模型(Netty+Reactor)实现,核心架构包含 3 个核心组件,流程如下:
客户端请求 → 网关接收请求 → 路由匹配(断言 Predicate) → 过滤器链执行(Filter) → 转发到微服务 → 微服务响应 → 过滤器链反向执行 → 响应客户端
user-service-route)。lb://user-service,lb 表示负载均衡)。/api/user/**、请求方法为 GET)。① 创建 Maven 项目,命名为 gateway-service,引入核心依赖:
<!-- Spring Boot 父依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/>
</parent>
<!-- Spring Cloud 依赖管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Cloud Gateway 核心依赖(注意:排除 spring-boot-starter-web,避免冲突) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Nacos 服务发现(用于动态路由,从 Nacos 获取微服务地址) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.0.4.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 日志依赖(SLF4J+Logback) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
⚠️ 注意:Spring Cloud Gateway 基于 Netty 实现,与 Spring MVC 的 spring-boot-starter-web 存在冲突,必须排除 spring-boot-starter-web 依赖,否则启动报错。
② 编写启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现(从 Nacos 获取微服务列表)
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
③ 编写基础配置文件 application.yml:
spring:
application:
name: gateway-service # 网关服务名称(注册到 Nacos)
cloud:
# Nacos 服务发现配置
nacos:
discovery:
server-addr: localhost:8848 # Nacos 地址
# Gateway 配置
gateway:
# 启用服务发现路由(自动从 Nacos 获取微服务,生成路由规则)
discovery:
locator:
enabled: true # 开启服务发现路由
lower-case-service-id: true # 微服务名称转为小写(如 USER-SERVICE→user-service)
# 路由规则配置(静态路由,优先级高于服务发现路由)
routes:
# 路由 1:用户服务路由
- id: user-service-route # 路由唯一 ID(自定义,建议与服务名一致)
uri: lb://user-service # 目标微服务地址(lb 表示负载均衡)
predicates:
- Path=/api/user/** # 路径断言:匹配以/api/user/开头的请求
filters:
- RewritePath=/api/user/(?<segment>.*), /${segment} # 路径重写:/api/user/1 → /1
# 路由 2:订单服务路由
- id: order-service-route
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- RewritePath=/api/order/(?<segment>.*), /${segment}
server:
port: 8080 # 网关端口(客户端统一访问 8080 端口)
# 日志配置(打印请求详细日志,便于调试)
logging:
level:
org.springframework.cloud.gateway: DEBUG
org.springframework.web.reactive: INFO
① 启动 Nacos Server(确保 user-service、order-service 已注册到 Nacos)。
② 启动 user-service(端口 8081)、order-service(端口 8082)。
③ 启动 gateway-service(端口 8080)。
使用 Postman 发送请求,验证路由转发功能:
http://localhost:8080/api/user/1user-service 的 /1 接口,返回用户信息:http://localhost:8080/api/order/1order-service 的 /1 接口,返回订单信息:访问订单服务接口:
{"orderId":1695888888888,"userId":1,"username":"张三 1","productName":"Java 编程思想","price":89.0,"count":1,"totalAmount":89.0,"status":"已创建"}
访问用户服务接口:
{"id":1,"username":"张三 1","phone":"13800138001","address":"北京市朝阳区"}
服务发现路由测试:
discovery.locator.enabled=true,网关会自动生成路由:http://localhost:8080/服务名/接口路径http://localhost:8080/user-service/user/1(直接通过服务名访问),返回结果与步骤 1 一致。Spring Cloud Gateway 内置了 11 种断言工厂(Predicate Factory),支持根据路径、请求头、请求参数、时间、Cookie 等条件匹配路由。断言工厂的命名格式为 XXXRoutePredicateFactory,配置时只需写前缀(如 Path 对应 PathRoutePredicateFactory)。
修改 application.yml 中的路由断言配置,演示常用断言的使用:
spring:
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
predicates:
# 1. 路径断言(必选,匹配/api/user/**或/user/**)
- Path=/api/user/**, /user/**
# 2. 方法断言(仅允许 GET 请求)
- Method=GET
# 3. 请求头断言(必须包含 X-Request-Id 请求头)
- Header=X-Request-Id, \d+ # 正则表达式:X-Request-Id 的值为数字
# 4. 请求参数断言(可选参数 token,值包含"auth")
- Query=token, auth.*
# 5. 时间断言(请求时间在 2024-01-01 之后)
- After=2024-01-01T00:00:00+08:00[Asia/Shanghai]
filters:
- RewritePath=/api/user/(?<segment>.*), /${segment}
- RewritePath=/user/(?<segment>.*), /${segment}
使用 Postman 发送请求,验证断言效果:
http://localhost:8080/api/user/1?token=auth123X-Request-Id: 123456X-Request-Id 请求头 → 路由不匹配,返回 404。token 参数值为 test123(不包含 auth) → 路由不匹配,返回 404。若内置断言无法满足需求,可自定义断言工厂。例如,实现一个'IP 白名单'断言,仅允许指定 IP 访问:
① 编写自定义断言工厂:
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import javax.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
// 断言工厂名称:IpWhiteListRoutePredicateFactory → 配置时使用 IpWhiteList
@Component
public class IpWhiteListRoutePredicateFactory extends AbstractRoutePredicateFactory<IpWhiteListRoutePredicateFactory.Config> {
// 配置类(接收 yaml 中的配置参数)
@Validated
public static class Config {
@NotEmpty(message = "IP 白名单不能为空")
private List<String> allowedIps;
public List<String> getAllowedIps() { return allowedIps; }
public void setAllowedIps(List<String> allowedIps) { this.allowedIps = allowedIps; }
}
// 构造方法:指定配置类
public IpWhiteListRoutePredicateFactory() {
super(Config.class);
}
// 配置参数解析(将 yaml 中的 allowedIps 转为 List<String>)
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("allowedIps");
}
// 断言逻辑:判断请求 IP 是否在白名单中
@Override
public GatewayPredicate apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// 获取客户端 IP(实际开发中需处理反向代理场景,如 Nginx 转发)
ServerHttpRequest request = exchange.getRequest();
String clientIp = request.getRemoteAddress().getAddress().getHostAddress();
// 判断 IP 是否在白名单中
return config.getAllowedIps().contains(clientIp);
}
@Override
public String toString() {
return "IpWhiteList: " + config.getAllowedIps();
}
};
}
}
② 配置自定义断言:
spring:
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/api/user/**
- IpWhiteList=127.0.0.1, 192.168.1.100 # 仅允许本地和 192.168.1.100 访问
filters:
- RewritePath=/api/user/(?<segment>.*), /${segment}
③ 测试效果:
Spring Cloud Gateway 的过滤器分为局部过滤器和全局过滤器,核心区别在于作用范围和配置方式:
| 过滤器类型 | 作用范围 | 配置方式 | 典型场景 |
|---|---|---|---|
| 局部过滤器 | 特定路由 | 在路由的 filters 节点中配置 | 路径重写、局部限流、特定路由的参数处理 |
| 全局过滤器 | 所有路由 | 实现 GlobalFilter 接口,无需配置 | 统一认证、日志收集、跨域处理、全局限流 |
Ordered 接口的 getOrder() 方法返回值排序(值越小,执行优先级越高)。局部过滤器仅对当前路由生效,Spring Cloud Gateway 内置了多种局部过滤器(如路径重写、请求头添加、参数添加等),以下是常用场景实战:
已在基础配置中演示,核心作用是将客户端请求路径改写为微服务的实际路径。例如:
/api/user/1 → 改写为 /1 → 转发到 user-service 的 /1 接口。RewritePath=原始路径正则,目标路径(使用 ${segment} 引用正则分组)。为转发到微服务的请求添加固定请求头,例如添加 X-Gateway-Version 标识网关版本:
spring:
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- RewritePath=/api/user/(?<segment>.*), /${segment}
- AddRequestHeader=X-Gateway-Version, v1.0.0 # 添加请求头
为请求添加固定参数,例如添加 source=gateway 标识请求来源:
filters:
- AddRequestParameter=source, gateway # 所有转发到 user-service 的请求都会携带 source=gateway 参数
实现一个局部过滤器,为特定路由的响应添加 X-Response-Time 头,记录请求处理耗时:
① 编写自定义局部过滤器:
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
// 局部过滤器工厂名称:ResponseTimeGatewayFilterFactory → 配置时使用 ResponseTime
@Component
public class ResponseTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<ResponseTimeGatewayFilterFactory.Config> {
// 配置类(无参数,仅作为标识)
public static class Config {}
public ResponseTimeGatewayFilterFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(); // 无配置参数,返回空列表
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 1. pre 逻辑:记录请求开始时间
long startTime = System.currentTimeMillis();
// 2. 转发请求到微服务
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 3. post 逻辑:计算耗时,添加响应头
long duration = System.currentTimeMillis() - startTime;
exchange.getResponse().getHeaders().add("X-Response-Time", duration + "ms");
}));
};
}
}
② 配置局部过滤器:
spring:
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- RewritePath=/api/user/(?<segment>.*), /${segment}
- ResponseTime # 引用自定义局部过滤器
③ 测试效果:
http://localhost:8080/api/user/1,查看响应头,会新增 X-Response-Time: 20ms(耗时根据实际情况变化)。全局过滤器对所有路由生效,是实现统一功能的核心手段。以下是两个最常用的全局过滤器实战:
微服务架构中,客户端登录成功后获取 JWT 令牌,后续所有请求都需在请求头中携带 Authorization: Bearer {token},网关统一校验令牌有效性,无效则直接返回 401,避免每个微服务重复开发认证逻辑。
① 引入 JWT 依赖:
<!-- JWT 依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
② 编写 JWT 工具类:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
@Component
public class JwtUtil {
// JWT 密钥(实际开发中需配置在 Nacos,避免硬编码)
@Value("${jwt.secret:abcdefghijklmnopqrstuvwxyz1234567890}")
private String secret;
// 令牌过期时间(2 小时)
@Value("${jwt.expire:7200000}")
private long expire;
// 生成密钥(HMAC-SHA256 算法要求密钥长度至少 256 位)
private SecretKey getSecretKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
// 生成 JWT 令牌
public String generateToken(Long userId, String username) {
return Jwts.builder()
.setSubject(userId.toString()) // 主题(存储用户 ID)
.claim("username", username) // 自定义声明(存储用户名)
.setIssuedAt(new Date()) // 签发时间
.setExpiration(new Date(System.currentTimeMillis() + expire)) // 过期时间
.signWith(getSecretKey()) // 签名
.compact();
}
// 校验令牌有效性
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token);
return true;
} catch (Exception e) {
// 令牌过期、签名错误等都会抛出异常
return false;
}
}
// 解析令牌中的声明
public Claims parseClaims(String token) {
return Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token).getBody();
}
}
③ 编写统一认证全局过滤器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Autowired
private JwtUtil jwtUtil;
// 不需要认证的接口(白名单)
private static final String[] WHITE_LIST = {
"/api/user/login", // 登录接口
"/api/user/register", // 注册接口
"/actuator/health" // 健康检查接口
};
@Override
public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 1. 判断请求路径是否在白名单中,若在则直接放行
String path = request.getPath().value();
for (String whitePath : WHITE_LIST) {
if (path.startsWith(whitePath)) {
return chain.filter(exchange);
}
}
// 2. 从请求头中获取 Authorization 令牌
String authHeader = request.getHeaders().getFirst("Authorization");
if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
// 3. 无令牌或令牌格式错误,返回 401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 4. 提取令牌(去掉"Bearer "前缀)
String token = authHeader.substring(7);
if (!jwtUtil.validateToken(token)) {
// 5. 令牌无效,返回 401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 6. 令牌有效,解析用户信息,添加到请求头(供微服务使用)
try {
String userId = jwtUtil.parseClaims(token).getSubject();
String username = jwtUtil.parseClaims(token).get("username", String.class);
// 向请求头添加用户信息(微服务可通过请求头获取,无需再次解析 JWT)
ServerHttpRequest newRequest = request.mutate()
.header("X-User-Id", userId)
.header("X-User-Name", username)
.build();
// 替换请求对象
return chain.filter(exchange.mutate().request(newRequest).build());
} catch (Exception e) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}
// 过滤器优先级:值越小越先执行(认证过滤器需优先执行)
@Override
public int getOrder() {
return -100;
}
}
④ 测试认证功能:
/api/user/login,传入用户名密码,返回令牌:获取 JWT 令牌(模拟登录接口,实际开发中由 user-service 实现):
{"code":200,"msg":"登录成功","data":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
http://localhost:8080/api/user/1Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...http://localhost:8080/api/user/1 → 返回 401 Unauthorized。收集所有请求的日志(请求路径、方法、IP、耗时、响应状态等),便于问题排查与系统监控:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.UUID;
@Component
public class LogGlobalFilter implements GlobalFilter, Ordered {
private static final Logger logger = LoggerFactory.getLogger(LogGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
// 1. 生成请求唯一 ID(用于追踪请求链路)
String requestId = UUID.randomUUID().toString();
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 2. pre 逻辑:记录请求信息
long startTime = System.currentTimeMillis();
String method = request.getMethodValue();
String path = request.getPath().value();
String clientIp = request.getRemoteAddress().getAddress().getHostAddress();
logger.info("【请求开始】requestId: {}, 客户端 IP: {}, 方法:{}, 路径:{}, 请求头:{}", requestId, clientIp, method, path, request.getHeaders());
// 3. 转发请求,监听响应完成事件
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 4. post 逻辑:记录响应信息
long duration = System.currentTimeMillis() - startTime;
int statusCode = response.getStatusCode().value();
logger.info("【请求结束】requestId: {}, 响应状态:{}, 耗时:{}ms", requestId, statusCode, duration);
}));
}
// 日志过滤器优先级低于认证过滤器(先认证,再记录日志)
@Override
public int getOrder() {
return -90;
}
}
启动网关后,访问任意接口,控制台会输出类似日志:
【请求开始】requestId: 550e8400-e29b-41d4-a716-446655440000, 客户端 IP: 127.0.0.1, 方法:GET, 路径:/api/user/1, 请求头:[Authorization:"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", X-Request-Id:"123456"] 【请求结束】requestId: 550e8400-e29b-41d4-a716-446655440000, 响应状态:200, 耗时:35ms
当客户端(如前端 Vue 项目)与网关不在同一域名时,会出现跨域请求问题。Spring Cloud Gateway 支持全局配置 CORS,无需每个微服务单独配置:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': # 对所有路径生效
allowed-origins: "*" # 允许所有来源(生产环境建议指定具体域名,如 https://www.example.com)
allowed-methods: "*" # 允许所有 HTTP 方法(GET、POST、PUT、DELETE 等)
allowed-headers: "*" # 允许所有请求头
allow-credentials: true # 允许携带 Cookie
max-age: 3600 # 预检请求缓存时间(1 小时)
基础路由配置写在 application.yml 中,修改后需重启网关才能生效,无法满足生产环境的动态调整需求。基于 Nacos 实现动态路由,可通过 Nacos 控制台修改路由配置,网关实时感知并生效,无需重启。
① 引入 Nacos Config 依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.0.4.0</version>
</dependency>
② 创建 bootstrap.yml 文件(加载 Nacos 配置):
spring:
application:
name: gateway-service
cloud:
nacos:
config:
server-addr: localhost:8848 # Nacos 配置中心地址
file-extension: yaml # 配置文件格式
namespace: public # 命名空间
group: DEFAULT_GROUP # 配置分组
profiles:
active: dev # 环境标识
③ 在 Nacos 控制台创建配置:
gateway-service-dev.yaml(格式:服务名 - 环境名。文件扩展名)。spring:
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- RewritePath=/api/user/(?<segment>.*), /${segment}
- id: order-service-route
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- RewritePath=/api/order/(?<segment>.*), /${segment}
Spring Cloud Gateway 与 Nacos Config 整合后,默认支持配置动态刷新,但路由规则的刷新需要自定义逻辑(监听 Nacos 配置变更,重新加载路由):
① 编写动态路由配置类:
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
@Component
public class DynamicRouteConfig implements ApplicationEventPublisherAware {
@Value("${spring.cloud.nacos.config.server-addr}")
private String serverAddr;
@Value("${spring.cloud.nacos.config.namespace}")
private String namespace;
@Value("${spring.cloud.nacos.config.group}")
private String group;
@Value("${spring.application.name}")
private String serviceName;
@Value("${spring.profiles.active}")
private String activeProfile;
// 路由定义写入器(用于更新路由)
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
// 事件发布器(用于发布路由刷新事件)
private ApplicationEventPublisher applicationEventPublisher;
// Nacos 配置服务
private ConfigService configService;
// 配置 Data ID
private String dataId;
@PostConstruct
public void init() throws NacosException {
// 初始化 Data ID
dataId = serviceName + "-" + activeProfile + ".yaml";
// 初始化 Nacos ConfigService
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
properties.put("namespace", namespace);
configService = NacosFactory.createConfigService(properties);
// 加载初始路由配置
loadRouteConfig();
// 注册 Nacos 配置监听器(监听配置变更)
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
// 配置变更时,重新加载路由
loadRouteConfig();
}
@Override
public Executor getExecutor() {
return null;
}
});
}
// 加载路由配置(简化版:实际开发中需解析 YAML 为 RouteDefinition 列表)
private void loadRouteConfig() {
try {
// 从 Nacos 获取配置
String config = configService.getConfig(dataId, group, 5000);
if (org.springframework.util.StringUtils.hasText(config)) {
// 解析 YAML 配置为 RouteDefinition 列表(此处省略解析逻辑,推荐使用 SnakeYAML)
// 假设解析后得到 List<RouteDefinition> routeDefinitionsList
List<RouteDefinition> routeDefinitions = parseYamlToRouteDefinitions(config);
// 1. 删除所有现有路由
routeDefinitionWriter.delete(Mono.just("user-service-route")).subscribe();
routeDefinitionWriter.delete(Mono.just("order-service-route")).subscribe();
// 2. 添加新路由
for (RouteDefinition routeDefinition : routeDefinitions) {
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
}
// 3. 发布路由刷新事件
applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
System.out.println("动态路由更新成功!");
}
} catch (Exception e) {
System.err.println("动态路由更新失败:" + e.getMessage());
}
}
// 解析 YAML 配置为 RouteDefinition 列表(实际开发中需实现)
private List<RouteDefinition> parseYamlToRouteDefinitions(String config) {
// 此处使用 Spring 的 YamlPropertiesFactoryBean 解析 YAML,转换为 RouteDefinition
// 简化示例,实际需根据配置结构编写解析逻辑
return null;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
⚠️ 注意:实际开发中,parseYamlToRouteDefinitions 方法需使用 SnakeYAML 或 Spring 的 YamlPropertiesFactoryBean 解析 Nacos 中的 YAML 配置,将其转换为 List<RouteDefinition> 对象,确保路由规则正确加载。
gateway-service-dev.yaml,新增一个商品服务路由:- id: product-service-route
uri: lb://product-service
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/product/(?<segment>.*), /${segment}
http://localhost:8080/api/product/1,网关会自动转发到 product-service,无需重启网关。网关作为流量入口,需要对进入系统的流量进行限流,避免后端微服务被压垮;同时,当后端微服务故障时,网关需要实现熔断,直接返回降级响应,避免故障扩散。Spring Cloud Gateway 与 Sentinel 整合,可快速实现限流与熔断功能。
<!-- Sentinel 核心依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2021.0.4.0</version>
</dependency>
<!-- Sentinel 与 Gateway 整合依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>2021.0.4.0</version>
</dependency>
<!-- Sentinel 控制台通信依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.6</version>
</dependency>
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # Sentinel 控制台地址(注意:与网关端口冲突时需修改 Sentinel 端口)
port: 8720 # 与 Sentinel 控制台通信的端口(默认 8719,避免冲突)
# 网关限流配置
scg:
fallback:
mode: response # 降级模式:返回自定义响应
response-status: 429 # 降级响应状态码
response-body: '{"code":429,"msg":"当前请求过于频繁,请稍后再试!"}' # 降级响应体
⚠️ 注意:Sentinel 控制台默认端口为 8080,与网关端口冲突,需修改 Sentinel 启动命令:
java -jar sentinel-dashboard-1.8.6.jar --server.port=8083
修改后 Sentinel 控制台地址为 http://localhost:8083,网关配置中的 spring.cloud.sentinel.transport.dashboard 需同步修改为 localhost:8083。
http://localhost:8080/api/user/1),触发 Sentinel 监控。/api/user/**(路由路径)。快速访问 http://localhost:8080/api/user/1(每秒超过 5 次),网关返回自定义降级响应:
{"code":429,"msg":"当前请求过于频繁,请稍后再试!"}
当后端微服务(如 user-service)故障时,网关熔断,避免频繁调用失败:
lb://user-service(微服务名称)。user-service 的接口,添加 2 秒延迟:@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id) throws InterruptedException {
Thread.sleep(2000); // 模拟慢调用
// 其余逻辑不变
}
http://localhost:8080/api/user/1(超过 5 次),触发熔断,网关返回降级响应。网关作为微服务架构的入口,一旦单点故障,整个系统将无法对外提供服务。因此,生产环境必须部署多个网关实例,配合负载均衡器(如 Nginx)实现高可用。
客户端 → Nginx(负载均衡) → 网关实例 1(8080) → 微服务集群
→ 网关实例 2(8081) → 微服务集群
→ 网关实例 3(8082) → 微服务集群
java -jar gateway-service.jar --server.port=8080java -jar gateway-service.jar --server.port=8081java -jar gateway-service.jar --server.port=8082nginx.conf:http {
# 网关集群配置
upstream gateway_cluster {
server 127.0.0.1:8080 weight=1; # 权重 1
server 127.0.0.1:8081 weight=1; # 权重 1
server 127.0.0.1:8082 weight=1; # 权重 1
}
server {
listen 80; # Nginx 监听 80 端口(客户端统一访问 80 端口)
server_name localhost;
location / {
# 转发请求到网关集群
proxy_pass http://gateway_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
http://localhost/api/user/1(Nginx 端口 80),Nginx 会将请求转发到任意一个网关实例。spring:
cloud:
gateway:
httpclient:
pool:
max-idle-time: 60000 # 连接池最大空闲时间(60 秒)
max-connections: 1000 # 每个路由的最大连接数
response-timeout: 5000 # 响应超时时间(5 秒)
server:
http2:
enabled: true # 启用 HTTP/2
spring-cloud-gateway-cache 组件实现网关缓存(需额外引入依赖)。logging:
level:
org.springframework.cloud.gateway: DEBUG
org.springframework.http.server.reactive: DEBUG
reactor.netty: DEBUG
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
endpoints:
web:
exposure:
include: gateway, health, info # 暴露 gateway 监控端点
endpoint:
gateway:
enabled: true # 启用网关监控
http://localhost:8080/actuator/gateway/routes,查看当前路由配置;访问 http://localhost:8080/actuator/gateway/globalfilters,查看全局过滤器。| 常见问题 | 解决方案 |
|---|---|
| 路由匹配失败(404) | 1. 检查路由 Path 断言是否正确; 2. 检查微服务是否已注册到 Nacos; 3. 开启 discovery.locator.enabled=true,测试服务名路由 |
| 跨域请求失败(CORS) | 检查网关全局 CORS 配置,确保 allowed-origins、allowed-methods 配置正确 |
| 认证失败(401) | 1. 检查 JWT 令牌是否有效; 2. 检查请求头 Authorization 格式是否正确(Bearer + 空格 + 令牌);3. 检查白名单配置是否包含当前路径 |
| 限流熔断不生效 | 1. 检查 Sentinel 依赖是否完整; 2. 确认已访问网关接口触发 Sentinel 监控; 3. 检查 Sentinel 控制台规则配置是否正确 |
| 网关响应缓慢 | 1. 排查后端微服务响应时间; 2. 检查过滤器链是否过长; 3. 调整 Netty 连接池参数 |
✅ 本章详细讲解了微服务网关的核心价值、Spring Cloud Gateway 的核心架构与实战应用,从基础路由配置到高级功能(动态路由、统一认证、限流熔断、高可用部署),完整覆盖了网关的设计与落地流程。
通过本章学习,读者应掌握:
网关作为微服务架构的'入口',是系统安全防护、流量控制的关键环节。下一章将讲解微服务监控与运维体系,包括 Prometheus + Grafana 监控指标收集、ELK 日志分析、SkyWalking 链路追踪等内容,帮助读者构建完整的微服务运维体系。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online