API 网关
API 网关是微服务架构中的重要组件,它作为系统的统一入口,负责请求路由、负载均衡、权限校验、日志记录等工作。
为什么需要 API 网关?
1. 统一入口
在没有网关的情况下,客户端需要知道每个微服务的地址:
无网关的架构(客户端直接调用):
┌──────────┐ ┌──────────┐
│ 客户端 │────►│ 用户服务 │
└──────────┘ └──────────┘
┌──────────┐ ┌──────────┐
│ 客户端 │────►│ 订单服务 │
└──────────┘ └──────────┘
┌──────────┐ ┌──────────┐
│ 客户端 │────►│ 商品服务 │
└──────────┘ └──────────┘
问题:
1. 客户端需要管理多个服务端点
2. 跨域问题
3. 认证授权逻辑重复
4. 难以统一监控和日志
2. 使用 API 网关
有网关的架构:
┌──────────┐
│ 客户端 │
└────┬─────┘
│
▼
┌─────────────────────┐
│ API 网关 │ ← 统一入口
│ (Gateway) │
└────┬────────┬────────┘
│ │ │
▼ ▼ ▼
┌────────┐┌────────┐┌────────┐
│用户服务 ││订单服务 ││商品服务 │
└────────┘└────────┘└────────┘
优势:
1. 客户端只需知道网关地址
2. 统一认证授权
3. 统一日志和监控
4. 减少客户端与服务的耦合
Spring Cloud Gateway 简介
Spring Cloud Gateway 是 Spring 官方开发的网关框架,基于 WebFlux(响应式编程)和 Project Reactor。
特点
- 基于 Spring Framework 5:使用函数式编程
- 基于 Project Reactor:完全异步非阻塞
- 动态路由:支持配置动态更新
- 内置 Predicates:支持多种路由匹配条件
- 内置 Filters:支持请求和响应的过滤
Spring Cloud Gateway 工作原理
请求流程:
┌──────────────┐
│ 客户端 │
└──────┬───────┘
│ HTTP/HTTPS
▼
┌──────────────────────────────────────────────────┐
│ Gateway Handler Mapping │
│ (根据 uri 匹配路由规则) │
└──────────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ Gateway Web Handler │
│ (执行前置过滤器链) │
└──────────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ Filter Chain │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Filter1 │→ │ Filter2 │→ │ Filter3 │→ ... │
│ │ (认证) │ │ (日志) │ │ (限流) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
└──────────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ Proxy Handler │
│ (转发到下游服务) │
└──────────────────────┬───────────────────────────┘
│
▼
┌──────────────┐
│ 下游服务 │
└──────────────┘
快速开始
1. 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 如果需要服务发现 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
2. 配置文件
server:
port: 8080
spring:
application:
name: gateway-service
cloud:
gateway:
discovery:
locator:
enabled: true # 启用服务发现
lower-case-service-id: true # 服务名小写
routes:
- id: user-service
uri: lb://user-service # lb 表示负载均衡
predicates:
- Path=/user/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
3. 启动类
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
路由配置详解
路由(Route)
路由是网关的基本组成单元,包含以下属性:
- id:路由唯一标识
- uri:目标服务地址
- predicates:路由匹配条件
- filters:过滤器
spring:
cloud:
gateway:
routes:
- id: my-route
uri: http://localhost:8081
predicates:
- Path=/api/**
filters:
- StripPrefix=1
谓词(Predicate)
Spring Cloud Gateway 内置多种谓词:
1. Path 路径匹配
predicates:
- Path=/user/**
- Path=/order/{id}
2. Method HTTP 方法
predicates:
- Method=GET,POST
3. Header 请求头
predicates:
- Header=X-Request-Id, \d+
4. Query 查询参数
predicates:
- Query=name
- Query=name, zhang.* # 正则匹配
5. After / Before / Between 时间
predicates:
- After=2024-01-01T00:00:00.000+08:00[Asia/Shanghai]
6. Cookie
predicates:
- Cookie=name, zhangsan
7. 组合多个谓词
spring:
cloud:
gateway:
routes:
- id: combine-predicates
uri: lb://user-service
predicates:
- Path=/user/**
- Method=GET
- Header=Authorization, Bearer.*
过滤器(Filter)
过滤器可以在请求前后执行自定义逻辑。
1. 内置过滤器
filters:
- StripPrefix=1 # 去掉路径前缀
- AddRequestHeader=X-Request-Id, ${UUID} # 添加请求头
- AddResponseHeader=X-Response-Id, ${UUID} # 添加响应头
- RemoveRequestHeader=X-Remove # 移除请求头
- SetStatus=200 # 设置响应状态
- SetResponseHeader=Content-Type, application/json # 设置响应头
- RedirectTo=302, https://example.com # 重定向
2. 自定义过滤器
// 全局过滤器
@Component
public class GlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
// 获取请求
ServerHttpRequest request = exchange.getRequest();
// 记录日志
System.out.println("请求路径: " + request.getURI());
// 业务逻辑
String token = request.getHeaders().getFirst("Authorization");
// 继续执行过滤器链
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -100; // 优先级,数字越小越先执行
}
}
// 局部过滤器
@Component
public class CustomGatewayFilterFactory
extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {
public CustomGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 前置逻辑
if (config.isEnabled()) {
System.out.println("前置过滤器执行");
}
// 执行后续过滤器
return chain.filter(exchange).then(
// 后置逻辑
Mono.fromRunnable(() -> {
if (config.isEnabled()) {
System.out.println("后置过滤器执行");
}
})
);
};
}
public static class Config {
private boolean enabled = true;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
服务认证与授权
1. JWT 认证
@Component
public class JwtAuthenticationFilter implements GlobalFilter {
@Autowired
private JwtUtils jwtUtils;
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
String token = getTokenFromRequest(exchange.getRequest());
if (token != null && jwtUtils.validateToken(token)) {
// 验证通过,将用户信息添加到请求属性
String userId = jwtUtils.getUserIdFromToken(token);
ServerHttpRequest request = exchange.getRequest()
.mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(request).build());
}
// 验证失败,返回 401
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
private String getTokenFromRequest(ServerHttpRequest request) {
String bearerToken = request.getHeaders().getFirst("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
2. 权限校验
@Component
public class PermissionFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
// 检查用户是否有权限访问该路径
if (!hasPermission(userId, path)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
private boolean hasPermission(String userId, String path) {
// 实际业务中查询权限
return true;
}
}
限流
使用 Redis 进行限流
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
</dependencies>
spring:
cloud:
gateway:
redis:
rate-limiter:
replenish-rate: 1 # 每秒补充令牌数
burst-capacity: 10 # 令牌桶容量
default-filters:
- name: RequestRateLimiter
args:
redis-rate-limiter:
replenish-rate: 10
burst-capacity: 20
requested-retokens: 1
自定义限流器
@Component
public class CustomRateLimiterGatewayFilterFactory
extends AbstractGatewayFilterFactory<CustomRateLimiterGatewayFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String key = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
int count = getCurrentCount(key);
if (count > config.getMaxRequests()) {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
incrementCount(key);
return chain.filter(exchange);
};
}
// 实现限流逻辑
private int getCurrentCount(String key) { return 0; }
private void incrementCount(String key) { }
public static class Config {
private int maxRequests = 100;
public int getMaxRequests() { return maxRequests; }
public void setMaxRequests(int maxRequests) { this.maxRequests = maxRequests; }
}
}
熔断与降级
使用 Resilience4j 集成
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
</dependencies>
spring:
cloud:
gateway:
default-filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fallback
@RestController
public class FallbackController {
@GetMapping("/fallback")
public Map<String, Object> fallback() {
Map<String, Object> result = new HashMap<>();
result.put("code", 503);
result.put("message", "服务暂时不可用,请稍后重试");
return result;
}
}
跨域配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
高可用
多实例部署
┌─────────────────────┐
│ 负载均衡器 │
│ (Nginx/F5/LB) │
└──────────┬──────────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌────────┐ ┌────────┐
│Gateway1│ │Gateway2│
│ 8080 │ │ 8081 │
└────────┘ └────────┘
最佳实践
- 路由设计:合理规划路由粒度,避免路由过多
- 限流配置:根据服务能力配置合适的限流规则
- 超时配置:合理设置超时时间,避免请求堆积
- 监控告警:监控网关的请求量、响应时间、错误率
- 日志记录:记录请求日志,便于问题排查
小结
本章主要介绍了:
- API 网关的作用:统一入口、认证授权、日志监控
- Spring Cloud Gateway:Spring 官方网关框架
- 路由配置:路由、谓词、过滤器
- 认证授权:JWT 认证、权限校验
- 限流与熔断:保护后端服务
- 高可用:多实例部署
API 网关是微服务架构的统一入口,是客户端与服务端之间的桥梁,合理使用网关可以大大提高系统的可维护性和安全性。