在传统单体架构中,应用通常作为一个整体部署运行,所有功能模块共享同一个代码库和数据库。这种架构下,客户端直接与单体应用通信,路由逻辑相对简单。但随着业务复杂度提升,单体架构逐渐暴露出扩展性差、技术栈单一、部署效率低等问题。
微服务架构通过将系统拆分为多个小型服务解决了这些问题,但同时也引入了新的挑战。想象一个电商系统被拆分为用户服务、商品服务、订单服务等独立模块后:
java复制// 客户端需要直接调用各个服务
String userServiceUrl = "http://user-service:8081";
String productServiceUrl = "http://product-service:8082";
String orderServiceUrl = "http://order-service:8083";
// 登录请求
Response loginResp = httpClient.post(userServiceUrl + "/login", credentials);
// 查询商品
Response productsResp = httpClient.get(productServiceUrl + "/products?category=electronics");
// 创建订单
Response orderResp = httpClient.post(orderServiceUrl + "/orders", orderData);
这种架构下至少存在六个显著问题:
API网关正是为解决这些问题而生的架构模式。它作为系统的唯一入口,承担了以下核心职责:
实际案例:某电商平台在引入网关后,客户端代码量减少40%,认证逻辑维护成本降低70%,全局限流使系统稳定性提升90%
Spring Cloud Gateway基于Reactor编程模型构建,其核心架构围绕以下三个概念展开:
路由(Route):定义请求如何转发的基本单元,包含:
断言(Predicate):Java 8的Predicate接口实现,决定请求是否匹配当前路由。支持:
过滤器(Filter):修改请求和响应的处理单元,分为:
java复制// 典型路由配置示例
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- AddRequestHeader=X-User-Type, member
当请求到达网关时,处理流程如下:
mermaid复制sequenceDiagram
participant C as Client
participant G as Gateway
participant S as Service
C->>G: HTTP Request
G->>G: Route Matching
G->>G: Pre Filters
G->>S: Forward Request
S->>G: Service Response
G->>G: Post Filters
G->>C: HTTP Response
Spring Cloud Gateway在性能方面做了多项优化:
实测对比(相同硬件环境):
| 网关类型 | RPS | 平均延迟 | 99分位延迟 |
|---|---|---|---|
| Zuul 1.x | 8,000 | 12ms | 45ms |
| Spring Cloud GW | 25,000 | 3ms | 15ms |
使用Spring Initializr创建项目时,需选择以下依赖:
关键pom.xml配置:
xml复制<dependencies>
<!-- 网关核心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 健康检查 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
yaml复制spring:
cloud:
gateway:
routes:
- id: legacy-system
uri: http://old.example.com
predicates:
- Path=/legacy/**
filters:
- RewritePath=/legacy/(?<segment>.*), /v1/$\{segment}
- id: websocket-route
uri: ws://chat-service:8080
predicates:
- Path=/ws/**
yaml复制spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/products/**
filters:
- CircuitBreaker(name: productCB, fallbackUri: forward:/fallback/product)
yaml复制 - id: canary-release
uri: lb://user-service
predicates:
- Path=/api/users/**
- Weight=user-service-canary, 10
- id: user-service-primary
uri: lb://user-service
predicates:
- Path=/api/users/**
- Weight=user-service-canary, 90
yaml复制spring:
cloud:
gateway:
default-filters:
- RemoveRequestHeader=Cookie,Set-Cookie # 移除敏感头
routes:
- id: auth-service
uri: lb://auth-service
predicates:
- Path=/oauth2/**
filters:
- TokenRelay # OAuth2令牌中继
java复制@Slf4j
@Component
public class RateLimitFilter implements GlobalFilter, Ordered {
private final RateLimiter rateLimiter;
public RateLimitFilter() {
this.rateLimiter = RateLimiter.create(1000); // 1000请求/秒
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (!rateLimiter.tryAcquire()) {
log.warn("Rate limit exceeded for {}", exchange.getRequest().getURI());
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE + 100;
}
}
java复制@Slf4j
@Component
public class JwtAuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().value();
// 跳过登录和公开接口
if (path.startsWith("/auth/login") || path.startsWith("/public")) {
return chain.filter(exchange);
}
// 获取并验证Token
String token = extractToken(exchange.getRequest());
if (token == null || !JwtUtil.validateToken(token)) {
return unauthorized(exchange);
}
// 添加用户信息到请求
String userId = JwtUtil.extractUserId(token);
ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
private String extractToken(ServerHttpRequest request) {
// 从Header或Cookie提取token
}
private Mono<Void> unauthorized(ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics,gateway
metrics:
tags:
application: ${spring.application.name}
关键监控指标:
http.server.requests:请求量统计gateway.requests:路由请求计数system.cpu.usage:CPU使用率jvm.memory.used:内存使用推荐部署架构:
code复制 +-----------------+
| Load Balancer |
+--------+--------+
|
+----------------+----------------+
| | |
+-----+------+ +-----+------+ +-----+------+
| Gateway 1 | | Gateway 2 | | Gateway N |
+------------+ +------------+ +------------+
配置建议:
/actuator/healthyaml复制resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: "0.5"
memory: 1Gi
问题1:路由不生效
检查步骤:
bash复制curl http://localhost:8888/actuator/gateway/routes
问题2:性能瓶颈
优化建议:
yaml复制spring:
cloud:
gateway:
filter:
cache:
enabled: true
size: 1000
ttl: 60s
问题3:跨域配置无效
正确配置示例:
yaml复制spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "https://example.com"
allowed-methods: "GET,POST"
allowed-headers: "*"
max-age: 3600
使用wrk进行压力测试:
bash复制wrk -t12 -c400 -d30s http://gateway:8888/api/products
| 参数 | 默认值 | 生产建议 | 说明 |
|---|---|---|---|
| reactor.netty.ioWorkerCount | CPU核心数 | 核心数*2 | IO线程数 |
| reactor.netty.pool.maxConnections | 500 | 1000 | 最大连接数 |
| spring.cloud.gateway.metrics.enabled | false | true | 开启指标收集 |
| server.netty.maxInitialLineLength | 4096 | 8192 | 最大HTTP初始行长度 |
配置示例:
yaml复制server:
netty:
max-initial-line-length: 8192
reactor:
netty:
io-worker-count: 8
pool:
max-connections: 1000
spring:
cloud:
gateway:
metrics:
enabled: true
某金融系统网关调优前后对比:
| 指标 | 调优前 | 调优后 | 提升幅度 |
|---|---|---|---|
| 吞吐量(RPS) | 12,000 | 35,000 | 292% |
| 平均延迟 | 45ms | 15ms | 300% |
| 99分位延迟 | 230ms | 80ms | 287% |
| CPU使用率 | 85% | 60% | -25% |
关键优化措施:
java复制@Component
public class MobilePredicateFactory extends AbstractRoutePredicateFactory<MobilePredicateFactory.Config> {
public MobilePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String userAgent = exchange.getRequest()
.getHeaders()
.getFirst("User-Agent");
return userAgent != null &&
userAgent.toLowerCase().contains("mobile");
};
}
public static class Config {
// 可配置参数
}
}
使用方式:
yaml复制predicates:
- name: Mobile
java复制@Component
public class LoggingFilterFactory extends AbstractGatewayFilterFactory<LoggingFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
long start = System.currentTimeMillis();
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
long duration = System.currentTimeMillis() - start;
log.info("{} {} - {}ms",
exchange.getRequest().getMethod(),
exchange.getRequest().getURI(),
duration);
}));
};
}
public static class Config {
// 可配置参数
}
}
xml复制<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
java复制@PostConstruct
public void initRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("user-service");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
主要变更点:
最低要求:
废弃配置:
yaml复制# 旧版
spring.cloud.gateway.discovery.locator.enabled: true
# 新版
spring.cloud.gateway.discovery.locator.enabled: true
spring.cloud.discovery.reactive.enabled: true
新特性:
迁移步骤:
替换依赖:
xml复制<!-- 移除 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!-- 添加 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
配置转换示例:
yaml复制# Zuul配置
zuul:
routes:
user-service:
path: /users/**
serviceId: user-service
# Gateway等效配置
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/users/**
过滤器迁移:
ZuulFilter → Gateway的GlobalFilter经过多个生产项目验证的黄金法则:
路由设计原则
安全防护要点
性能优化清单
yaml复制server:
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,application/json
监控告警关键指标
灾难恢复策略
yaml复制- id: fallback
uri: http://fallback-service
predicates:
- Path=/fallback/**
filters:
- SetStatus=503
方案一:基于Header的路由
yaml复制spring:
cloud:
gateway:
routes:
- id: canary-user-service
uri: lb://user-service-v2
predicates:
- Path=/api/users/**
- Header=X-Canary, true
- id: primary-user-service
uri: lb://user-service-v1
predicates:
- Path=/api/users/**
方案二:基于权重的路由
yaml复制 - id: canary-20%
uri: lb://user-service-v2
predicates:
- Path=/api/users/**
- Weight=user-service, 20
- id: primary-80%
uri: lb://user-service-v1
predicates:
- Path=/api/users/**
- Weight=user-service, 80
问题:文件上传可能占用大量内存
解决方案:
yaml复制spring:
webflux:
max-in-memory-size: 10MB # 默认256KB
java复制@Bean
public ServerCodecConfigurer serverCodecConfigurer() {
return ServerCodecConfigurer.create()
.defaultCodecs()
.maxInMemorySize(10 * 1024 * 1024);
}
yaml复制spring:
cloud:
gateway:
httpclient:
connect-timeout: 5000
response-timeout: 30s
配置示例:
yaml复制spring:
cloud:
gateway:
routes:
- id: websocket-route
uri: ws://chat-service:8080
predicates:
- Path=/ws/**
filters:
- StripPrefix=1
优化建议:
yaml复制spring:
cloud:
gateway:
httpclient:
websocket:
ping-interval: 30s
java复制@Bean
public MeterRegistryCustomizer<MeterRegistry> metrics() {
return registry -> registry.config().commonTags("application", "gateway");
}
实际项目中的演进路径示例:
code复制Year 1: 基础路由 + 安全防护
Year 2: 全链路监控 + 智能限流
Year 3: 多云架构支持 + 服务网格集成
Year 4: AI驱动的动态路由优化
yaml复制spring:
cloud:
gateway:
routes:
# 用户服务
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- CircuitBreaker(name: userCB, fallbackUri: forward:/fallback/user)
# 商品服务
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/products/**
filters:
- StripPrefix=1
- RequestRateLimiter(redis-rate-limiter.replenishRate=100)
# 订单服务
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Method=POST,PUT,DELETE
filters:
- StripPrefix=1
- JwtAuth
# 支付服务
- id: payment-service
uri: lb://payment-service
predicates:
- Path=/api/payments/**
filters:
- StripPrefix=1
- SecureHeaders
yaml复制routes:
# 设备接入
- id: device-ingress
uri: lb://device-service
predicates:
- Path=/device/telemetry/**
filters:
- DeviceAuth
- MessageCompression
# 命令下发
- id: command-delivery
uri: lb://command-service
predicates:
- Path=/device/command/**
filters:
- JwtAuth
- CommandValidation
# 规则引擎
- id: rule-engine
uri: lb://rule-engine-service
predicates:
- Path=/rules/**
filters:
- StripPrefix=1
java复制@Bean
public SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/public/**").permitAll()
.pathMatchers("/api/**").authenticated()
.anyExchange().denyAll()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthConverter())
.and()
.csrf().disable()
.build();
}
private Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthConverter() {
// 自定义JWT转换逻辑
}
默认配置可能不匹配生产需求,建议调整:
yaml复制reactor:
netty:
io-worker-count: 16 # IO线程数 (通常CPU核心数*2)
pool:
max-connections: 1024 # 最大连接数
acquire-timeout: 5000 # 连接获取超时(ms)
server:
netty:
max-initial-line-length: 8192 # 最大初始行长度
max-header-size: 16KB # 最大header大小
启用路由缓存提升性能:
yaml复制spring:
cloud:
gateway:
route-definition-locator:
cache:
enabled: true
initial-capacity: 50
maximum-size: 100
expire-after-write: 5m
避免阻塞操作:
java复制// 错误示例 - 阻塞调用
Mono<String> result = Mono.fromCallable(() -> {
return blockingHttpCall(); // 阻塞IO
}).subscribeOn(Schedulers.boundedElastic());
// 正确做法 - 使用非阻塞客户端
WebClient client = WebClient.create();
Mono<String> result = client.get()
.uri("http://service/api")
.retrieve()
.bodyToMono(String.class);
常见泄漏场景及解决方案:
未释放网络资源:
java复制// 确保所有Flux/Mono都有终止条件
Flux.interval(Duration.ofSeconds(1))
.take(10) // 必须有终止条件
.subscribe();
大对象缓存:
java复制// 使用软引用缓存
Cache<String, Object> cache = CacheBuilder.newBuilder()
.softValues()
.build();
线程局部变量:
java复制// 使用Context代替ThreadLocal
Mono.deferContextual(ctx -> {
String value = ctx.get("key");
return Mono.just(value);
});
yaml复制spring:
cloud:
gateway:
httpclient:
secure:
ssl:
use-insecure-trust-manager: false
handshake-timeout: 10000
close-notify-flush-timeout: 3000
close-notify-read-timeout: 0
java复制@Bean
public GlobalFilter sqlInjectionFilter() {
return (exchange, chain) -> {
String query = exchange.getRequest().getURI().getQuery();
if (query != null && containsSqlInjection(query)) {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
};
}
集成Alibaba Anti-DDoS方案:
java复制@Bean
public GlobalFilter antiDDoSFilter() {
return new GlobalFilter() {
private final ConcurrentHashMap<String, AtomicInteger> counters = new ConcurrentHashMap<>();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String ip = getClientIp(exchange.getRequest());
AtomicInteger counter = counters.computeIfAbsent(ip, k -> new AtomicInteger(0));
if (counter.incrementAndGet() > 100) { // 100请求/秒
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange)
.doFinally(signal -> counter.decrementAndGet());
}
};
}
code复制 +---------+ +---------+
| Client | | API |
+----+----+ +----+----+
| |
| 1. 请求(无token) |
+----------------->
| |
| 2. 重定向到登录 |
<-----------------+
| |
+-------+ 3. 登录 +---------+ |
| Auth +<-------->| User | |
+---+---+ +---------+ |
| |
| 4. 颁发token |
+----------------------------->
| |
| 5. 携带token访问 |
+----------------->
| |
| 6. 验证token |
| +--------+ |
+--->| Gateway |
+--------+
xml复制<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
yaml复制spring:
sleuth:
sampler:
probability: 1.0
zipkin:
base-url: http://zipkin:9411
java复制@Slf4j
@Component
public class TraceLogFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Span span = exchange.getAttribute(Span.class.getName());
log.info("TraceID: {}", span.context().traceId());
return chain.filter(exchange);
}
}
java复制@Bean
public ErrorWebExceptionHandler customExceptionHandler() {
return new JsonExceptionHandler();
}
public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {
@Override
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Throwable error = getError(request);
HttpStatus status = determineHttpStatus(error);
String code = determineErrorCode(error);
String message = determineMessage(error);
Map<String, Object> body = Map.of(
"timestamp", Instant.now(),
"status", status.value(),
"code", code,
"message", message,
"path", request.path()
);
return ServerResponse.status(status)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(body);
}
private HttpStatus determineHttpStatus(Throwable error) {
if (error instanceof RateLimiterException) {
return HttpStatus.TOO_MANY_REQUESTS;
}
// 其他异常处理...
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
yaml复制spring:
cloud:
gateway:
routes:
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/products/**
filters:
- name: CircuitBreaker
args:
name: productCB
fallbackUri: forward:/fallback/product
statusCodes: 500,502,503,504
降级控制器示例:
java复制@RestController
public class FallbackController {
@GetMapping("/fallback/product")
public Mono<Map<String, Object>> productFallback() {
return Mono.just(Map.of(
"status", 503,
"message", "服务暂时不可用,请稍后重试",
"timestamp", Instant.now()
));
}
}
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway
spec:
replicas: 3
selector:
matchLabels:
app: gateway
template:
metadata:
labels:
app: gateway
spec:
containers:
- name: gateway
image: registry.example.com/gateway:1.0.0
ports:
- containerPort: 8888
resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: "0.5"
memory: 1Gi
livenessProbe:
httpGet:
path: /actuator/health
port: 8888
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8888
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: gateway
spec:
selector:
app: gateway
ports:
- protocol: TCP
port: 80
targetPort: 8888
type: LoadBalancer
yaml复制# .gitlab-ci.yml
stages:
- build
- test
- deploy
build:
stage: build
image: maven:3.8.4-jdk-11
script:
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
test:
stage: test
image: maven:3.8.4-jdk-11
script:
- mvn test
- mvn sonar:sonar -Dsonar.host.url=$SONAR_URL
deploy:
stage: deploy
image: kubectl:1.22
script:
- kubectl set image deployment/gateway gateway=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- kubectl rollout status deployment/gate