Skip to content

Spring Cloud 网关

1. API 网关概述

1.1 什么是 API 网关

API 网关是微服务架构的统一入口,负责请求路由、协议转换、认证授权、限流熔断等功能。

1.2 网关核心功能

功能说明
路由转发请求路由到后端服务
负载均衡多实例负载均衡
认证授权统一身份认证
限流熔断保护后端服务
日志监控请求日志记录
协议转换HTTP/gRPC 转换

2. Spring Cloud Gateway

2.1 添加依赖

xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

2.2 基本路由配置

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
        
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
        
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/api/products/**

2.3 路由谓词

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: path-route
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
        
        - id: method-route
          uri: lb://user-service
          predicates:
            - Method=GET,POST
        
        - id: header-route
          uri: lb://user-service
          predicates:
            - Header=X-Request-Id, \d+
        
        - id: query-route
          uri: lb://user-service
          predicates:
            - Query=token
        
        - id: time-route
          uri: lb://user-service
          predicates:
            - After=2024-01-01T00:00:00+08:00[Asia/Shanghai]
        
        - id: cookie-route
          uri: lb://user-service
          predicates:
            - Cookie=session, .+
        
        - id: host-route
          uri: lb://user-service
          predicates:
            - Host=**.example.com
        
        - id: remote-addr-route
          uri: lb://user-service
          predicates:
            - RemoteAddr=192.168.1.1/24

2.4 组合谓词

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: combined-route
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
            - Method=GET
            - Header=X-API-Version, v1

3. 过滤器

3.1 内置过滤器

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - AddRequestHeader=X-Request-Foo, Bar
            - AddRequestParameter=foo, bar
            - AddResponseHeader=X-Response-Foo, Bar
            - StripPrefix=1
            - PrefixPath=/api
            - RewritePath=/api/(?<segment>.*), /$\{segment}
            - SetPath=/api/{segment}
            - SetRequestHeader=X-Request-Id, ${value}
            - SetResponseHeader=X-Response-Id, ${value}
            - SetStatus=200
            - RequestRateLimiter=10,20
            - CircuitBreaker=myCircuitBreaker

3.2 自定义过滤器

java
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        
        if (token == null || !validateToken(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        
        return chain.filter(exchange);
    }
    
    @Override
    public int getOrder() {
        return -100;
    }
    
    private boolean validateToken(String token) {
        return token.startsWith("Bearer ");
    }
}

3.3 网关过滤器工厂

java
@Component
public class RequestTimeGatewayFilterFactory 
        extends AbstractGatewayFilterFactory<RequestTimeConfig> {
    
    public RequestTimeGatewayFilterFactory() {
        super(RequestTimeConfig.class);
    }
    
    @Override
    public GatewayFilter apply(RequestTimeConfig config) {
        return (exchange, chain) -> {
            long startTime = System.currentTimeMillis();
            
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                long duration = System.currentTimeMillis() - startTime;
                exchange.getResponse().getHeaders()
                    .add("X-Response-Time", duration + "ms");
            }));
        };
    }
}

public class RequestTimeConfig {
    private boolean showArgs;
    // getter and setter
}

4. 限流

4.1 基于 Redis 限流

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
yaml
spring:
  redis:
    host: localhost
    port: 6379
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                redis-rate-limiter.requestedTokens: 1
                key-resolver: "#{@userKeyResolver}"
java
@Configuration
public class RateLimiterConfig {
    
    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> {
            String userId = exchange.getRequest()
                .getHeaders()
                .getFirst("X-User-Id");
            return Mono.just(userId != null ? userId : "anonymous");
        };
    }
    
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest()
                .getRemoteAddress()
                .getAddress()
                .getHostAddress()
        );
    }
    
    @Bean
    public KeyResolver apiKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest()
                .getPath()
                .value()
        );
    }
}

4.2 自定义限流

java
@Component
public class CustomRateLimiter implements RateLimiter {
    
    private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
    
    @Override
    public Mono<Response> isAllowed(String routeId, String id) {
        RateLimiter limiter = limiters.computeIfAbsent(id, 
            k -> RateLimiter.create(10.0));
        
        boolean allowed = limiter.tryAcquire();
        return Mono.just(new Response(allowed, 
            allowed ? 0 : 1));
    }
}

5. 熔断降级

5.1 添加依赖

xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>

5.2 配置熔断

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - name: CircuitBreaker
              args:
                name: userServiceCircuitBreaker
                fallbackUri: forward:/fallback/users

resilience4j:
  circuitbreaker:
    configs:
      default:
        slidingWindowSize: 10
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
        permittedNumberOfCallsInHalfOpenState: 5
    instances:
      userServiceCircuitBreaker:
        baseConfig: default

5.3 降级处理

java
@RestController
public class FallbackController {
    
    @GetMapping("/fallback/users")
    public Mono<Map<String, Object>> userFallback() {
        return Mono.just(Map.of(
            "code", 503,
            "message", "用户服务暂时不可用",
            "data", Collections.emptyList()
        ));
    }
    
    @GetMapping("/fallback/orders")
    public Mono<Map<String, Object>> orderFallback() {
        return Mono.just(Map.of(
            "code", 503,
            "message", "订单服务暂时不可用",
            "data", Collections.emptyList()
        ));
    }
}

6. 认证授权

6.1 JWT 认证过滤器

java
@Component
public class JwtAuthFilter implements GlobalFilter, Ordered {
    
    private final JwtTokenProvider tokenProvider;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getPath().value();
        
        if (isPublicPath(path)) {
            return chain.filter(exchange);
        }
        
        String token = extractToken(exchange);
        
        if (token == null || !tokenProvider.validateToken(token)) {
            return unauthorized(exchange);
        }
        
        String userId = tokenProvider.getUserIdFromToken(token);
        ServerHttpRequest request = exchange.getRequest().mutate()
            .header("X-User-Id", userId)
            .build();
        
        return chain.filter(exchange.mutate().request(request).build());
    }
    
    private boolean isPublicPath(String path) {
        return path.startsWith("/api/auth/") || 
               path.startsWith("/api/public/");
    }
    
    private String extractToken(ServerWebExchange exchange) {
        String bearer = exchange.getRequest()
            .getHeaders()
            .getFirst("Authorization");
        
        if (bearer != null && bearer.startsWith("Bearer ")) {
            return bearer.substring(7);
        }
        return null;
    }
    
    private Mono<Void> unauthorized(ServerWebExchange exchange) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        exchange.getResponse().getHeaders()
            .setContentType(MediaType.APPLICATION_JSON);
        
        String body = "{\"code\":401,\"message\":\"Unauthorized\"}";
        DataBuffer buffer = exchange.getResponse()
            .bufferFactory()
            .wrap(body.getBytes());
        
        return exchange.getResponse().writeWith(Mono.just(buffer));
    }
    
    @Override
    public int getOrder() {
        return -100;
    }
}

7. 跨域配置

7.1 全局跨域

yaml
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOriginPatterns: "*"
            allowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
              - OPTIONS
            allowedHeaders: "*"
            allowCredentials: true
            maxAge: 3600

7.2 代码配置

java
@Configuration
public class CorsConfig {
    
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOriginPatterns(List.of("*"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        config.setAllowedHeaders(List.of("*"));
        config.setAllowCredentials(true);
        config.setMaxAge(3600L);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        
        return new CorsWebFilter(source);
    }
}

8. 日志记录

8.1 请求日志过滤器

java
@Component
public class RequestLogFilter implements GlobalFilter, Ordered {
    
    private static final Logger log = LoggerFactory.getLogger(RequestLogFilter.class);
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        
        long startTime = System.currentTimeMillis();
        String requestId = UUID.randomUUID().toString();
        
        log.info("Request: {} {} - {}", 
            request.getMethod(), 
            request.getPath(), 
            requestId);
        
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            long duration = System.currentTimeMillis() - startTime;
            int statusCode = exchange.getResponse().getStatusCode().value();
            
            log.info("Response: {} - {} ms - {}", 
                statusCode, 
                duration, 
                requestId);
        }));
    }
    
    @Override
    public int getOrder() {
        return -200;
    }
}

9. 动态路由

9.1 动态路由配置

java
@Service
public class DynamicRouteService {
    
    private final RouteDefinitionLocator routeDefinitionLocator;
    private final RouteDefinitionWriter routeDefinitionWriter;
    
    public void addRoute(RouteDefinition definition) {
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
    }
    
    public void updateRoute(RouteDefinition definition) {
        deleteRoute(definition.getId());
        addRoute(definition);
    }
    
    public void deleteRoute(String routeId) {
        routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
    }
    
    public Flux<RouteDefinition> getRoutes() {
        return routeDefinitionLocator.getRouteDefinitions();
    }
}

@RestController
@RequestMapping("/routes")
public class RouteController {
    
    private final DynamicRouteService routeService;
    
    @PostMapping
    public String addRoute(@RequestBody RouteDefinition definition) {
        routeService.addRoute(definition);
        return "success";
    }
    
    @DeleteMapping("/{id}")
    public String deleteRoute(@PathVariable String id) {
        routeService.deleteRoute(id);
        return "success";
    }
}

10. 小结

本章学习了 Spring Cloud Gateway 的核心内容:

内容要点
路由配置谓词、URI、优先级
过滤器内置过滤器、自定义过滤器
限流Redis 限流、自定义限流
熔断降级Resilience4j、降级处理
认证授权JWT 认证、权限校验
跨域配置全局跨域、代码配置
日志记录请求日志、响应日志
动态路由动态添加、更新、删除

下一章将学习 Spring Cloud 服务调用。