4.1 API网关概述

什么是API网关

API网关是微服务架构中的重要组件,作为系统的统一入口,负责请求路由、协议转换、安全认证、限流熔断等功能。

API网关的作用

┌─────────────────────────────────────────────────────────────────┐
│                        API网关架构图                            │
├─────────────────────────────────────────────────────────────────┤
│  客户端                                                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │
│  │ Web Browser │  │ Mobile App  │  │ Third Party │             │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘             │
│         │                │                │                    │
│         └────────────────┼────────────────┘                    │
│                          │                                     │
│                          ▼                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                  API Gateway                            │   │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │   │
│  │  │   路由管理   │ │   安全认证   │ │     限流熔断        │ │   │
│  │  └─────────────┘ └─────────────┘ └─────────────────────┘ │   │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │   │
│  │  │   协议转换   │ │   负载均衡   │ │     监控日志        │ │   │
│  │  └─────────────┘ └─────────────┘ └─────────────────────┘ │   │
│  └─────────────────────┬───────────────────────────────────┘   │
├────────────────────────┼───────────────────────────────────────┤
│                        │ 内部网络                               │
│                        ▼                                       │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐ │
│  │ User Service    │  │ Product Service │  │ Order Service   │ │
│  │ 192.168.1.10    │  │ 192.168.1.20    │  │ 192.168.1.30    │ │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

核心功能

  1. 请求路由:根据请求路径将请求转发到对应的后端服务
  2. 负载均衡:在多个服务实例之间分发请求
  3. 安全认证:统一的身份验证和授权
  4. 限流熔断:保护后端服务免受过载
  5. 协议转换:支持HTTP、WebSocket等多种协议
  6. 监控日志:统一的请求日志和监控指标
  7. 缓存:减少对后端服务的请求
  8. 跨域处理:统一处理CORS问题

4.2 Spring Cloud Gateway

Gateway简介

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,基于Spring 5、Spring Boot 2和Project Reactor构建,提供了简单而有效的方式来路由API请求。

核心概念

  1. Route(路由):网关的基本构建块,包含ID、目标URI、断言集合和过滤器集合
  2. Predicate(断言):匹配HTTP请求的条件
  3. Filter(过滤器):对请求和响应进行修改

项目搭建

1. 创建Gateway项目

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>api-gateway</artifactId>
    <version>1.0.0</version>
    <name>api-gateway</name>
    <description>API Gateway Service</description>
    
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
    </properties>
    
    <dependencies>
        <!-- Spring Cloud Gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        
        <!-- Eureka Client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        
        <!-- LoadBalancer -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        
        <!-- Redis for Rate Limiting -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
        
        <!-- Security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        
        <!-- 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>
        
        <!-- Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <!-- Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 启动类

ApiGatewayApplication.java

package com.example.apigateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * API网关启动类
 */
@SpringBootApplication
@EnableEurekaClient
public class ApiGatewayApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
}

4.3 路由配置

基础路由配置

application.yml

server:
  port: 8080

spring:
  application:
    name: api-gateway
  
  cloud:
    gateway:
      # 全局CORS配置
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "*"
            allowed-methods:
              - GET
              - POST
              - PUT
              - DELETE
              - OPTIONS
            allowed-headers: "*"
            allow-credentials: true
            max-age: 3600
      
      # 路由配置
      routes:
        # 用户服务路由
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                redis-rate-limiter.requestedTokens: 1
                key-resolver: "#{@userKeyResolver}"
        
        # 商品服务路由
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/api/products/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 20
                redis-rate-limiter.burstCapacity: 40
                redis-rate-limiter.requestedTokens: 1
                key-resolver: "#{@ipKeyResolver}"
        
        # 订单服务路由
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=1
            - AuthenticationFilter
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 5
                redis-rate-limiter.burstCapacity: 10
                redis-rate-limiter.requestedTokens: 1
                key-resolver: "#{@userKeyResolver}"
        
        # 认证服务路由
        - id: auth-service
          uri: lb://auth-service
          predicates:
            - Path=/api/auth/**
          filters:
            - StripPrefix=1
        
        # WebSocket路由
        - id: websocket-service
          uri: lb:ws://websocket-service
          predicates:
            - Path=/ws/**
        
        # 静态资源路由
        - id: static-resources
          uri: http://static.example.com
          predicates:
            - Path=/static/**
          filters:
            - StripPrefix=1
            - name: Hystrix
              args:
                name: static-fallback
                fallbackUri: forward:/fallback/static
      
      # 默认过滤器
      default-filters:
        - AddRequestHeader=X-Gateway-Name, api-gateway
        - AddRequestHeader=X-Request-Time, #{T(java.time.Instant).now().toString()}
        - AddResponseHeader=X-Response-Time, #{T(java.time.Instant).now().toString()}
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
            methods: GET,POST
            backoff:
              firstBackoff: 10ms
              maxBackoff: 50ms
              factor: 2
              basedOnPreviousValue: false
  
  # Redis配置
  redis:
    host: localhost
    port: 6379
    password: 
    database: 0
    timeout: 2000ms
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms

# Eureka配置
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.application.name}:${server.port}

# 日志配置
logging:
  level:
    org.springframework.cloud.gateway: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.netty: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

# 监控配置
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always
    gateway:
      enabled: true

动态路由配置

1. 路由配置类

GatewayConfig.java

package com.example.apigateway.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;

import java.time.Duration;

/**
 * 网关路由配置
 */
@Configuration
public class GatewayConfig {
    
    /**
     * 编程式路由配置
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            // 用户服务路由
            .route("user-service-route", r -> r
                .path("/api/users/**")
                .and()
                .method(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE)
                .uri("lb://user-service")
                .filters(f -> f
                    .stripPrefix(1)
                    .addRequestHeader("X-Service", "user-service")
                    .addResponseHeader("X-Response-Service", "user-service")
                    .retry(config -> config
                        .setRetries(3)
                        .setStatuses(org.springframework.http.HttpStatus.BAD_GATEWAY)
                        .setBackoff(Duration.ofMillis(100), Duration.ofMillis(1000), 2, false)
                    )
                )
            )
            
            // 商品服务路由 - 支持版本控制
            .route("product-service-v1", r -> r
                .path("/api/v1/products/**")
                .uri("lb://product-service-v1")
                .filters(f -> f
                    .stripPrefix(2)
                    .addRequestHeader("X-API-Version", "v1")
                )
            )
            .route("product-service-v2", r -> r
                .path("/api/v2/products/**")
                .uri("lb://product-service-v2")
                .filters(f -> f
                    .stripPrefix(2)
                    .addRequestHeader("X-API-Version", "v2")
                )
            )
            
            // 基于请求头的路由
            .route("mobile-api", r -> r
                .path("/api/mobile/**")
                .and()
                .header("User-Agent", ".*Mobile.*")
                .uri("lb://mobile-service")
                .filters(f -> f
                    .stripPrefix(2)
                    .addRequestHeader("X-Client-Type", "mobile")
                )
            )
            
            // 基于查询参数的路由
            .route("beta-features", r -> r
                .path("/api/**")
                .and()
                .query("beta", "true")
                .uri("lb://beta-service")
                .filters(f -> f
                    .addRequestHeader("X-Beta-User", "true")
                )
            )
            
            // 基于时间的路由(限时功能)
            .route("time-based-route", r -> r
                .path("/api/promotion/**")
                .and()
                .between(
                    java.time.ZonedDateTime.now(),
                    java.time.ZonedDateTime.now().plusDays(7)
                )
                .uri("lb://promotion-service")
                .filters(f -> f
                    .stripPrefix(2)
                    .addRequestHeader("X-Promotion-Active", "true")
                )
            )
            
            // 权重路由(灰度发布)
            .route("weight-high", r -> r
                .path("/api/test/**")
                .and()
                .weight("group1", 8)
                .uri("lb://test-service-stable")
                .filters(f -> f
                    .stripPrefix(2)
                    .addRequestHeader("X-Version", "stable")
                )
            )
            .route("weight-low", r -> r
                .path("/api/test/**")
                .and()
                .weight("group1", 2)
                .uri("lb://test-service-canary")
                .filters(f -> f
                    .stripPrefix(2)
                    .addRequestHeader("X-Version", "canary")
                )
            )
            
            .build();
    }
}

2. 自定义断言工厂

CustomRoutePredicateFactory.java

package com.example.apigateway.predicate;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * 自定义路由断言工厂
 * 支持基于用户角色的路由
 */
@Component
@Slf4j
public class UserRoleRoutePredicateFactory 
    extends AbstractRoutePredicateFactory<UserRoleRoutePredicateFactory.Config> {
    
    public UserRoleRoutePredicateFactory() {
        super(Config.class);
    }
    
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("roles");
    }
    
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            // 从请求头中获取用户角色
            String userRoles = exchange.getRequest().getHeaders().getFirst("X-User-Roles");
            
            if (userRoles == null) {
                log.debug("No user roles found in request headers");
                return false;
            }
            
            // 检查用户是否具有所需角色
            List<String> userRoleList = Arrays.asList(userRoles.split(","));
            boolean hasRole = config.getRoles().stream()
                .anyMatch(userRoleList::contains);
            
            log.debug("User roles: {}, Required roles: {}, Has access: {}", 
                     userRoles, config.getRoles(), hasRole);
            
            return hasRole;
        };
    }
    
    @Data
    public static class Config {
        private List<String> roles;
    }
}

4.4 过滤器

全局过滤器

1. 认证过滤器

AuthenticationGlobalFilter.java

package com.example.apigateway.filter;

import com.example.apigateway.util.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
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;

import java.util.Arrays;
import java.util.List;

/**
 * 全局认证过滤器
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class AuthenticationGlobalFilter implements GlobalFilter, Ordered {
    
    private final JwtUtil jwtUtil;
    
    // 不需要认证的路径
    private static final List<String> SKIP_AUTH_PATHS = Arrays.asList(
        "/api/auth/login",
        "/api/auth/register",
        "/api/auth/refresh",
        "/api/public",
        "/actuator",
        "/swagger",
        "/v3/api-docs"
    );
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        
        log.debug("Processing request: {} {}", request.getMethod(), path);
        
        // 跳过不需要认证的路径
        if (shouldSkipAuth(path)) {
            log.debug("Skipping authentication for path: {}", path);
            return chain.filter(exchange);
        }
        
        // 获取Authorization头
        String authHeader = request.getHeaders().getFirst("Authorization");
        
        if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
            log.warn("Missing or invalid Authorization header for path: {}", path);
            return handleUnauthorized(exchange);
        }
        
        // 提取JWT token
        String token = authHeader.substring(7);
        
        try {
            // 验证token
            if (!jwtUtil.validateToken(token)) {
                log.warn("Invalid JWT token for path: {}", path);
                return handleUnauthorized(exchange);
            }
            
            // 提取用户信息
            String userId = jwtUtil.getUserIdFromToken(token);
            String username = jwtUtil.getUsernameFromToken(token);
            List<String> roles = jwtUtil.getRolesFromToken(token);
            
            // 添加用户信息到请求头
            ServerHttpRequest modifiedRequest = request.mutate()
                .header("X-User-ID", userId)
                .header("X-Username", username)
                .header("X-User-Roles", String.join(",", roles))
                .build();
            
            log.debug("Authentication successful for user: {} ({})", username, userId);
            
            return chain.filter(exchange.mutate().request(modifiedRequest).build());
            
        } catch (Exception e) {
            log.error("Authentication error for path: {}", path, e);
            return handleUnauthorized(exchange);
        }
    }
    
    private boolean shouldSkipAuth(String path) {
        return SKIP_AUTH_PATHS.stream().anyMatch(path::startsWith);
    }
    
    private Mono<Void> handleUnauthorized(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json");
        
        String body = "{\"error\":\"Unauthorized\",\"message\":\"Authentication required\"}";
        
        return response.writeWith(
            Mono.just(response.bufferFactory().wrap(body.getBytes()))
        );
    }
    
    @Override
    public int getOrder() {
        return -100; // 高优先级,在其他过滤器之前执行
    }
}

2. 日志过滤器

LoggingGlobalFilter.java

package com.example.apigateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
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.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

/**
 * 全局日志过滤器
 */
@Component
@Slf4j
public class LoggingGlobalFilter implements GlobalFilter, Ordered {
    
    private static final String REQUEST_ID_HEADER = "X-Request-ID";
    private static final String START_TIME_ATTR = "startTime";
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        
        // 生成请求ID
        String requestId = request.getHeaders().getFirst(REQUEST_ID_HEADER);
        if (requestId == null) {
            requestId = UUID.randomUUID().toString();
        }
        
        // 记录开始时间
        long startTime = System.currentTimeMillis();
        exchange.getAttributes().put(START_TIME_ATTR, startTime);
        
        // 添加请求ID到请求头
        ServerHttpRequest modifiedRequest = request.mutate()
            .header(REQUEST_ID_HEADER, requestId)
            .build();
        
        // 记录请求信息
        log.info("[{}] Request: {} {} from {}", 
                requestId,
                request.getMethod(),
                request.getURI(),
                getClientIp(request));
        
        return chain.filter(exchange.mutate().request(modifiedRequest).build())
            .then(Mono.fromRunnable(() -> {
                // 记录响应信息
                ServerHttpResponse response = exchange.getResponse();
                Long startTimeAttr = exchange.getAttribute(START_TIME_ATTR);
                long duration = startTimeAttr != null ? 
                    System.currentTimeMillis() - startTimeAttr : 0;
                
                log.info("[{}] Response: {} {} in {}ms", 
                        requestId,
                        response.getStatusCode(),
                        request.getURI().getPath(),
                        duration);
                
                // 添加响应头
                response.getHeaders().add(REQUEST_ID_HEADER, requestId);
                response.getHeaders().add("X-Response-Time", String.valueOf(duration));
            }));
    }
    
    private String getClientIp(ServerHttpRequest request) {
        String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeaders().getFirst("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        return request.getRemoteAddress() != null ? 
            request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
    }
    
    @Override
    public int getOrder() {
        return -200; // 最高优先级
    }
}

自定义过滤器

1. 限流过滤器

RateLimitGatewayFilterFactory.java

package com.example.apigateway.filter;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;

/**
 * 自定义限流过滤器
 */
@Component
@Slf4j
public class RateLimitGatewayFilterFactory 
    extends AbstractGatewayFilterFactory<RateLimitGatewayFilterFactory.Config> {
    
    private final ReactiveRedisTemplate<String, String> redisTemplate;
    private final RedisScript<List> rateLimitScript;
    
    public RateLimitGatewayFilterFactory(ReactiveRedisTemplate<String, String> redisTemplate) {
        super(Config.class);
        this.redisTemplate = redisTemplate;
        this.rateLimitScript = createRateLimitScript();
    }
    
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("capacity", "refillTokens", "refillPeriod");
    }
    
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String key = getKey(exchange, config);
            
            return redisTemplate.execute(rateLimitScript, 
                Arrays.asList(key),
                String.valueOf(config.getCapacity()),
                String.valueOf(config.getRefillTokens()),
                String.valueOf(config.getRefillPeriod().getSeconds()))
                .cast(List.class)
                .flatMap(results -> {
                    boolean allowed = (Boolean) results.get(0);
                    long tokensLeft = (Long) results.get(1);
                    
                    if (allowed) {
                        log.debug("Rate limit passed for key: {}, tokens left: {}", key, tokensLeft);
                        return chain.filter(exchange);
                    } else {
                        log.warn("Rate limit exceeded for key: {}", key);
                        exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                        return exchange.getResponse().setComplete();
                    }
                });
        };
    }
    
    private String getKey(org.springframework.web.server.ServerWebExchange exchange, Config config) {
        // 根据配置的键类型生成限流键
        switch (config.getKeyType()) {
            case IP:
                return "rate_limit:ip:" + getClientIp(exchange.getRequest());
            case USER:
                String userId = exchange.getRequest().getHeaders().getFirst("X-User-ID");
                return "rate_limit:user:" + (userId != null ? userId : "anonymous");
            case PATH:
                return "rate_limit:path:" + exchange.getRequest().getURI().getPath();
            default:
                return "rate_limit:global";
        }
    }
    
    private String getClientIp(org.springframework.http.server.reactive.ServerHttpRequest request) {
        String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        return request.getRemoteAddress() != null ? 
            request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
    }
    
    private RedisScript<List> createRateLimitScript() {
        String script = 
            "local key = KEYS[1]\n" +
            "local capacity = tonumber(ARGV[1])\n" +
            "local refill_tokens = tonumber(ARGV[2])\n" +
            "local refill_period = tonumber(ARGV[3])\n" +
            "local current_time = redis.call('TIME')\n" +
            "local current_timestamp = current_time[1]\n" +
            "\n" +
            "local bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\n" +
            "local tokens = tonumber(bucket[1]) or capacity\n" +
            "local last_refill = tonumber(bucket[2]) or current_timestamp\n" +
            "\n" +
            "local time_passed = current_timestamp - last_refill\n" +
            "local new_tokens = math.min(capacity, tokens + (time_passed / refill_period) * refill_tokens)\n" +
            "\n" +
            "local allowed = new_tokens >= 1\n" +
            "if allowed then\n" +
            "    new_tokens = new_tokens - 1\n" +
            "end\n" +
            "\n" +
            "redis.call('HMSET', key, 'tokens', new_tokens, 'last_refill', current_timestamp)\n" +
            "redis.call('EXPIRE', key, refill_period * 2)\n" +
            "\n" +
            "return {allowed, new_tokens}";
        
        return RedisScript.of(script, List.class);
    }
    
    @Data
    public static class Config {
        private int capacity = 10;  // 桶容量
        private int refillTokens = 1;  // 每次补充的令牌数
        private Duration refillPeriod = Duration.ofSeconds(1);  // 补充周期
        private KeyType keyType = KeyType.IP;  // 限流键类型
        
        public enum KeyType {
            IP, USER, PATH, GLOBAL
        }
    }
}

2. 缓存过滤器

CacheGatewayFilterFactory.java

package com.example.apigateway.filter;

import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;

/**
 * 缓存过滤器
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class CacheGatewayFilterFactory 
    extends AbstractGatewayFilterFactory<CacheGatewayFilterFactory.Config> {
    
    private final ReactiveRedisTemplate<String, String> redisTemplate;
    
    public CacheGatewayFilterFactory() {
        super(Config.class);
    }
    
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("ttl", "keyPrefix");
    }
    
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // 只缓存GET请求
            if (!HttpMethod.GET.equals(exchange.getRequest().getMethod())) {
                return chain.filter(exchange);
            }
            
            String cacheKey = generateCacheKey(exchange, config);
            
            // 尝试从缓存获取
            return redisTemplate.opsForValue().get(cacheKey)
                .flatMap(cachedResponse -> {
                    log.debug("Cache hit for key: {}", cacheKey);
                    
                    // 返回缓存的响应
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.OK);
                    response.getHeaders().add("Content-Type", "application/json");
                    response.getHeaders().add("X-Cache", "HIT");
                    
                    DataBuffer buffer = response.bufferFactory().wrap(cachedResponse.getBytes());
                    return response.writeWith(Mono.just(buffer));
                })
                .switchIfEmpty(
                    // 缓存未命中,继续处理请求并缓存响应
                    chain.filter(exchange).then(Mono.fromRunnable(() -> {
                        log.debug("Cache miss for key: {}", cacheKey);
                        
                        // 这里简化处理,实际应该拦截响应体进行缓存
                        // 由于Spring Cloud Gateway的响应式特性,缓存响应体比较复杂
                        // 可以考虑使用其他方案如在业务服务中实现缓存
                        
                        exchange.getResponse().getHeaders().add("X-Cache", "MISS");
                    }))
                );
        };
    }
    
    private String generateCacheKey(org.springframework.web.server.ServerWebExchange exchange, Config config) {
        String path = exchange.getRequest().getURI().getPath();
        String query = exchange.getRequest().getURI().getQuery();
        
        StringBuilder keyBuilder = new StringBuilder(config.getKeyPrefix())
            .append(":")
            .append(path);
        
        if (query != null && !query.isEmpty()) {
            keyBuilder.append("?").append(query);
        }
        
        return keyBuilder.toString();
    }
    
    @Data
    public static class Config {
        private Duration ttl = Duration.ofMinutes(5);  // 缓存TTL
        private String keyPrefix = "gateway:cache";  // 缓存键前缀
    }
}

4.5 限流与熔断

Redis限流配置

1. 限流键解析器

KeyResolverConfig.java

package com.example.apigateway.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

/**
 * 限流键解析器配置
 */
@Configuration
public class KeyResolverConfig {
    
    /**
     * 基于IP的限流
     */
    @Bean
    @Primary
    public KeyResolver ipKeyResolver() {
        return exchange -> {
            String xForwardedFor = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
            String clientIp;
            
            if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
                clientIp = xForwardedFor.split(",")[0].trim();
            } else {
                clientIp = exchange.getRequest().getRemoteAddress() != null ?
                    exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() : "unknown";
            }
            
            return Mono.just(clientIp);
        };
    }
    
    /**
     * 基于用户的限流
     */
    @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 pathKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
    }
    
    /**
     * 基于API Key的限流
     */
    @Bean
    public KeyResolver apiKeyResolver() {
        return exchange -> {
            String apiKey = exchange.getRequest().getHeaders().getFirst("X-API-Key");
            return Mono.just(apiKey != null ? apiKey : "no-api-key");
        };
    }
    
    /**
     * 组合限流键
     */
    @Bean
    public KeyResolver compositeKeyResolver() {
        return exchange -> {
            String userId = exchange.getRequest().getHeaders().getFirst("X-User-ID");
            String path = exchange.getRequest().getURI().getPath();
            
            String key = (userId != null ? userId : "anonymous") + ":" + path;
            return Mono.just(key);
        };
    }
}

熔断器配置

1. Resilience4j配置

CircuitBreakerConfig.java

package com.example.apigateway.config;

import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;

/**
 * 熔断器配置
 */
@Configuration
public class CircuitBreakerConfig {
    
    /**
     * 默认熔断器配置
     */
    @Bean
    public Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {
        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
            .circuitBreakerConfig(io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.custom()
                .slidingWindowSize(10)  // 滑动窗口大小
                .slidingWindowType(SlidingWindowType.COUNT_BASED)  // 基于计数的滑动窗口
                .minimumNumberOfCalls(5)  // 最小调用次数
                .failureRateThreshold(50)  // 失败率阈值50%
                .waitDurationInOpenState(Duration.ofSeconds(30))  // 熔断器打开状态等待时间
                .permittedNumberOfCallsInHalfOpenState(3)  // 半开状态允许的调用次数
                .automaticTransitionFromOpenToHalfOpenEnabled(true)  // 自动从打开状态转换到半开状态
                .build())
            .timeLimiterConfig(TimeLimiterConfig.custom()
                .timeoutDuration(Duration.ofSeconds(10))  // 超时时间
                .build())
            .build());
    }
    
    /**
     * 用户服务熔断器配置
     */
    @Bean
    public Customizer<ReactiveResilience4JCircuitBreakerFactory> userServiceCustomizer() {
        return factory -> factory.configure(builder -> builder
            .circuitBreakerConfig(io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.custom()
                .slidingWindowSize(20)
                .minimumNumberOfCalls(10)
                .failureRateThreshold(60)
                .waitDurationInOpenState(Duration.ofSeconds(60))
                .build())
            .timeLimiterConfig(TimeLimiterConfig.custom()
                .timeoutDuration(Duration.ofSeconds(5))
                .build()), "user-service");
    }
    
    /**
     * 订单服务熔断器配置
     */
    @Bean
    public Customizer<ReactiveResilience4JCircuitBreakerFactory> orderServiceCustomizer() {
        return factory -> factory.configure(builder -> builder
            .circuitBreakerConfig(io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.custom()
                .slidingWindowSize(15)
                .minimumNumberOfCalls(8)
                .failureRateThreshold(40)
                .waitDurationInOpenState(Duration.ofSeconds(45))
                .build())
            .timeLimiterConfig(TimeLimiterConfig.custom()
                .timeoutDuration(Duration.ofSeconds(8))
                .build()), "order-service");
    }
}

2. 降级处理

FallbackController.java

package com.example.apigateway.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 降级处理控制器
 */
@RestController
@RequestMapping("/fallback")
@Slf4j
public class FallbackController {
    
    /**
     * 用户服务降级
     */
    @GetMapping("/user-service")
    public ResponseEntity<Map<String, Object>> userServiceFallback() {
        log.warn("User service fallback triggered");
        
        Map<String, Object> response = new HashMap<>();
        response.put("error", "User service is temporarily unavailable");
        response.put("message", "Please try again later");
        response.put("timestamp", LocalDateTime.now());
        response.put("service", "user-service");
        
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
    }
    
    /**
     * 商品服务降级
     */
    @GetMapping("/product-service")
    public ResponseEntity<Map<String, Object>> productServiceFallback() {
        log.warn("Product service fallback triggered");
        
        Map<String, Object> response = new HashMap<>();
        response.put("error", "Product service is temporarily unavailable");
        response.put("message", "Please try again later");
        response.put("timestamp", LocalDateTime.now());
        response.put("service", "product-service");
        
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
    }
    
    /**
     * 订单服务降级
     */
    @GetMapping("/order-service")
    public ResponseEntity<Map<String, Object>> orderServiceFallback() {
        log.warn("Order service fallback triggered");
        
        Map<String, Object> response = new HashMap<>();
        response.put("error", "Order service is temporarily unavailable");
        response.put("message", "Your order will be processed when service is restored");
        response.put("timestamp", LocalDateTime.now());
        response.put("service", "order-service");
        
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
    }
    
    /**
     * 默认降级
     */
    @GetMapping("/default")
    public ResponseEntity<Map<String, Object>> defaultFallback() {
        log.warn("Default fallback triggered");
        
        Map<String, Object> response = new HashMap<>();
        response.put("error", "Service temporarily unavailable");
        response.put("message", "Please try again later");
        response.put("timestamp", LocalDateTime.now());
        
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
    }
    
    /**
     * 静态资源降级
     */
    @GetMapping("/static")
    public ResponseEntity<Map<String, Object>> staticFallback() {
        log.warn("Static resource fallback triggered");
        
        Map<String, Object> response = new HashMap<>();
        response.put("error", "Static resource service unavailable");
        response.put("message", "Using cached version");
        response.put("timestamp", LocalDateTime.now());
        
        return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(response);
    }
}

4.6 安全配置

JWT工具类

JwtUtil.java

package com.example.apigateway.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.List;

/**
 * JWT工具类
 */
@Component
@Slf4j
public class JwtUtil {
    
    @Value("${jwt.secret:mySecretKey123456789012345678901234567890}")
    private String secret;
    
    @Value("${jwt.expiration:86400}")
    private Long expiration;
    
    private SecretKey getSigningKey() {
        return Keys.hmacShaKeyFor(secret.getBytes());
    }
    
    /**
     * 验证token
     */
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            log.error("Invalid JWT token: {}", e.getMessage());
            return false;
        }
    }
    
    /**
     * 从token中获取用户ID
     */
    public String getUserIdFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("userId", String.class);
    }
    
    /**
     * 从token中获取用户名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getSubject();
    }
    
    /**
     * 从token中获取角色列表
     */
    @SuppressWarnings("unchecked")
    public List<String> getRolesFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("roles", List.class);
    }
    
    /**
     * 从token中获取过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }
    
    /**
     * 检查token是否过期
     */
    public boolean isTokenExpired(String token) {
        Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }
    
    private Claims getClaimsFromToken(String token) {
        return Jwts.parserBuilder()
            .setSigningKey(getSigningKey())
            .build()
            .parseClaimsJws(token)
            .getBody();
    }
}

安全配置

SecurityConfig.java

package com.example.apigateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

/**
 * 安全配置
 */
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
            .csrf().disable()
            .cors().configurationSource(corsConfigurationSource())
            .and()
            .authorizeExchange()
            .pathMatchers("/actuator/**", "/fallback/**").permitAll()
            .anyExchange().permitAll()  // 由自定义过滤器处理认证
            .and()
            .build();
    }
    
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOriginPatterns(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        configuration.setMaxAge(3600L);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

4.7 监控与管理

健康检查

GatewayHealthIndicator.java

package com.example.apigateway.health;

import lombok.RequiredArgsConstructor;
import org.springframework.boot.actuator.health.Health;
import org.springframework.boot.actuator.health.ReactiveHealthIndicator;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.time.Duration;

/**
 * 网关健康检查
 */
@Component
@RequiredArgsConstructor
public class GatewayHealthIndicator implements ReactiveHealthIndicator {
    
    private final RouteLocator routeLocator;
    private final ReactiveRedisTemplate<String, String> redisTemplate;
    
    @Override
    public Mono<Health> health() {
        return checkRoutes()
            .zipWith(checkRedis())
            .map(tuple -> {
                boolean routesHealthy = tuple.getT1();
                boolean redisHealthy = tuple.getT2();
                
                if (routesHealthy && redisHealthy) {
                    return Health.up()
                        .withDetail("routes", "All routes are configured")
                        .withDetail("redis", "Redis connection is healthy")
                        .build();
                } else {
                    return Health.down()
                        .withDetail("routes", routesHealthy ? "OK" : "Some routes are not configured")
                        .withDetail("redis", redisHealthy ? "OK" : "Redis connection failed")
                        .build();
                }
            })
            .onErrorReturn(Health.down().withDetail("error", "Health check failed").build());
    }
    
    private Mono<Boolean> checkRoutes() {
        return routeLocator.getRoutes()
            .collectList()
            .map(routes -> !routes.isEmpty())
            .timeout(Duration.ofSeconds(5))
            .onErrorReturn(false);
    }
    
    private Mono<Boolean> checkRedis() {
        return redisTemplate.opsForValue()
            .set("health:check", "ok", Duration.ofSeconds(10))
            .timeout(Duration.ofSeconds(3))
            .map(result -> true)
            .onErrorReturn(false);
    }
}

监控指标

GatewayMetricsConfig.java

package com.example.apigateway.config;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 网关监控指标配置
 */
@Component
@RequiredArgsConstructor
public class GatewayMetricsFilter implements GlobalFilter, Ordered {
    
    private final MeterRegistry meterRegistry;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        String method = exchange.getRequest().getMethod().name();
        
        Timer.Sample sample = Timer.start(meterRegistry);
        
        return chain.filter(exchange)
            .doFinally(signalType -> {
                String status = exchange.getResponse().getStatusCode() != null ?
                    exchange.getResponse().getStatusCode().toString() : "unknown";
                
                sample.stop(Timer.builder("gateway.requests")
                    .tag("method", method)
                    .tag("path", path)
                    .tag("status", status)
                    .register(meterRegistry));
                
                // 记录请求计数
                meterRegistry.counter("gateway.requests.total",
                    "method", method,
                    "path", path,
                    "status", status)
                    .increment();
            });
    }
    
    @Override
    public int getOrder() {
        return -1;
    }
}

4.8 总结

核心概念回顾

  1. API网关作用

    • 统一入口:所有外部请求的单一入口点
    • 路由转发:根据规则将请求转发到后端服务
    • 安全认证:统一的身份验证和授权
    • 限流熔断:保护后端服务免受过载
    • 监控日志:统一的请求监控和日志记录
  2. Spring Cloud Gateway核心组件

    • Route(路由):定义请求如何转发到后端服务
    • Predicate(断言):匹配HTTP请求的条件
    • Filter(过滤器):对请求和响应进行处理
  3. 过滤器类型

    • 全局过滤器:应用于所有路由
    • 路由过滤器:应用于特定路由
    • 内置过滤器:Spring Cloud Gateway提供的过滤器
    • 自定义过滤器:根据业务需求开发的过滤器

最佳实践

  1. 路由设计

    • 使用有意义的路由ID
    • 合理设计URL路径规则
    • 考虑版本控制和向后兼容
    • 使用断言组合实现复杂路由逻辑
  2. 安全配置

    • 实施统一的身份验证
    • 使用HTTPS保护数据传输
    • 配置适当的CORS策略
    • 实施API密钥管理
  3. 性能优化

    • 配置合适的连接池大小
    • 使用缓存减少后端请求
    • 实施适当的限流策略
    • 监控和优化响应时间
  4. 容错处理

    • 配置熔断器保护后端服务
    • 实施优雅的降级策略
    • 设置合理的超时时间
    • 提供有意义的错误响应
  5. 监控运维

    • 配置健康检查端点
    • 收集关键性能指标
    • 实施日志聚合和分析
    • 设置告警机制

注意事项

  1. 性能考虑

    • Gateway基于Netty,适合高并发场景
    • 避免在过滤器中执行阻塞操作
    • 合理配置内存和线程池
  2. 配置管理

    • 使用配置中心管理路由配置
    • 支持动态路由更新
    • 区分不同环境的配置
  3. 安全防护

    • 防止SQL注入和XSS攻击
    • 实施请求大小限制
    • 配置IP白名单/黑名单
  4. 故障排查

    • 启用详细的调试日志
    • 使用链路追踪工具
    • 监控关键指标和告警

扩展方向

  1. 高级功能

    • 实施API版本管理
    • 集成服务网格
    • 支持GraphQL网关
    • 实现智能路由
  2. 集成组件

    • 集成配置中心(Nacos、Apollo)
    • 集成链路追踪(Zipkin、Jaeger)
    • 集成监控系统(Prometheus、Grafana)
    • 集成日志系统(ELK Stack)

通过本章学习,你应该掌握了Spring Cloud Gateway的核心概念和实际应用。API网关是微服务架构中的关键组件,合理的设计和配置能够显著提升系统的可用性、安全性和可维护性。

下一章我们将学习配置中心,了解如何在微服务架构中实现统一的配置管理。