API网关详解
API网关是微服务架构中的重要组件,作为系统的统一入口,负责请求路由、负载均衡、认证授权、限流熔断等功能。
核心价值
API网关 = 统一入口 + 服务治理 + 安全控制 + 性能优化
- 🌐 统一入口:为所有微服务提供统一的访问入口
- 🔒 安全控制:集中处理认证、授权、加密等安全功能
- ⚡ 性能优化:负载均衡、缓存、压缩等性能优化
- 🛡️ 服务治理:限流、熔断、监控、日志等治理功能
1. API网关核心功能
1.1 请求路由
API网关根据请求的URL、头部信息等将请求路由到相应的后端服务。
Spring Cloud Gateway路由配置
java
1@Configuration2public class GatewayConfig {3 4 @Bean5 public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {6 return builder.routes()7 // 用户服务路由8 .route("user-service", r -> r9 .path("/api/users/**")10 .uri("lb://user-service"))11 12 // 订单服务路由13 .route("order-service", r -> r14 .path("/api/orders/**")15 .uri("lb://order-service"))16 17 // 支付服务路由18 .route("payment-service", r -> r19 .path("/api/payments/**")20 .uri("lb://payment-service"))21 22 .build();23 }24}1.2 负载均衡
API网关可以在多个服务实例之间分发请求,提高系统的可用性和性能。
负载均衡配置
java
1@Configuration2public class LoadBalancerConfig {3 4 @Bean5 @LoadBalanced6 public RestTemplate restTemplate() {7 return new RestTemplate();8 }9 10 // 自定义负载均衡策略11 @Bean12 public ReactorLoadBalancer<ServiceInstance> userServiceLoadBalancer(13 Environment environment,14 LoadBalancerClientFactory loadBalancerClientFactory) {15 16 String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);17 return new RoundRobinLoadBalancer(18 loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),19 name);20 }21}1.3 认证授权
API网关可以集中处理认证和授权逻辑,确保只有合法用户才能访问系统。
JWT认证过滤器
java
1@Component2public class JwtAuthenticationFilter implements GlobalFilter, Ordered {3 4 private final JwtTokenProvider jwtTokenProvider;5 6 public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {7 this.jwtTokenProvider = jwtTokenProvider;8 }9 10 @Override11 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {12 ServerHttpRequest request = exchange.getRequest();13 14 // 跳过不需要认证的路径15 if (isPublicPath(request.getPath().value())) {16 return chain.filter(exchange);17 }18 19 // 获取Authorization头20 String authHeader = request.getHeaders().getFirst("Authorization");21 if (authHeader == null || !authHeader.startsWith("Bearer ")) {22 return handleUnauthorized(exchange);23 }24 25 String token = authHeader.substring(7);26 27 try {28 // 验证JWT token29 if (jwtTokenProvider.validateToken(token)) {30 // 提取用户信息并添加到请求头31 String userId = jwtTokenProvider.getUserIdFromToken(token);32 String userRole = jwtTokenProvider.getUserRoleFromToken(token);33 34 ServerHttpRequest modifiedRequest = request.mutate()35 .header("X-User-Id", userId)36 .header("X-User-Role", userRole)37 .build();38 39 return chain.filter(exchange.mutate().request(modifiedRequest).build());40 } else {41 return handleUnauthorized(exchange);42 }43 } catch (Exception e) {44 return handleUnauthorized(exchange);45 }46 }47 48 private boolean isPublicPath(String path) {49 return path.startsWith("/api/auth/") || 50 path.startsWith("/api/public/") ||51 path.equals("/health");52 }53 54 private Mono<Void> handleUnauthorized(ServerWebExchange exchange) {55 ServerHttpResponse response = exchange.getResponse();56 response.setStatusCode(HttpStatus.UNAUTHORIZED);57 response.getHeaders().add("Content-Type", "application/json");58 59 String body = "{\"error\":\"Unauthorized\",\"message\":\"Invalid or missing token\"}";60 DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());61 return response.writeWith(Mono.just(buffer));62 }63 64 @Override65 public int getOrder() {66 return -100; // 高优先级,在其他过滤器之前执行67 }68}2. 限流与熔断
2.1 限流实现
Redis限流过滤器
java
1@Component2public class RateLimitFilter implements GlobalFilter, Ordered {3 4 private final RedisTemplate<String, String> redisTemplate;5 private final RedisScript<Long> rateLimitScript;6 7 public RateLimitFilter(RedisTemplate<String, String> redisTemplate) {8 this.redisTemplate = redisTemplate;9 this.rateLimitScript = createRateLimitScript();10 }11 12 @Override13 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {14 ServerHttpRequest request = exchange.getRequest();15 String clientId = getClientId(request);16 String path = request.getPath().value();17 18 // 构建限流key19 String rateLimitKey = "rate_limit:" + clientId + ":" + path;20 21 // 执行限流检查22 Long currentRequests = redisTemplate.execute(23 rateLimitScript,24 Collections.singletonList(rateLimitKey),25 "10", // 限制:10次/分钟26 "60" // 时间窗口:60秒27 );28 29 if (currentRequests != null && currentRequests > 10) {30 return handleRateLimitExceeded(exchange);31 }32 33 return chain.filter(exchange);34 }35 36 private String getClientId(ServerHttpRequest request) {37 // 优先使用API Key38 String apiKey = request.getHeaders().getFirst("X-API-Key");39 if (apiKey != null) {40 return "api_key:" + apiKey;41 }42 43 // 使用IP地址作为fallback44 String clientIp = getClientIp(request);45 return "ip:" + clientIp;46 }47 48 private String getClientIp(ServerHttpRequest request) {49 String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");50 if (xForwardedFor != null && !xForwardedFor.isEmpty()) {51 return xForwardedFor.split(",")[0].trim();52 }53 54 String xRealIp = request.getHeaders().getFirst("X-Real-IP");55 if (xRealIp != null && !xRealIp.isEmpty()) {56 return xRealIp;57 }58 59 return request.getRemoteAddress() != null ? 60 request.getRemoteAddress().getAddress().getHostAddress() : "unknown";61 }62 63 private RedisScript<Long> createRateLimitScript() {64 String script = 65 "local key = KEYS[1] " +66 "local limit = tonumber(ARGV[1]) " +67 "local window = tonumber(ARGV[2]) " +68 "local current = redis.call('incr', key) " +69 "if current == 1 then " +70 " redis.call('expire', key, window) " +71 "end " +72 "return current";73 74 return RedisScript.of(script, Long.class);75 }76 77 private Mono<Void> handleRateLimitExceeded(ServerWebExchange exchange) {78 ServerHttpResponse response = exchange.getResponse();79 response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);80 response.getHeaders().add("Content-Type", "application/json");81 82 String body = "{\"error\":\"Rate limit exceeded\",\"message\":\"Too many requests\"}";83 DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());84 return response.writeWith(Mono.just(buffer));85 }86 87 @Override88 public int getOrder() {89 return -50;90 }91}2.2 熔断器实现
熔断器过滤器
java
1@Component2public class CircuitBreakerFilter implements GlobalFilter, Ordered {3 4 private final CircuitBreakerRegistry circuitBreakerRegistry;5 6 public CircuitBreakerFilter(CircuitBreakerRegistry circuitBreakerRegistry) {7 this.circuitBreakerRegistry = circuitBreakerRegistry;8 }9 10 @Override11 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {12 String serviceName = getServiceName(exchange.getRequest());13 CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(serviceName);14 15 return circuitBreaker.executeSupplier(() -> chain.filter(exchange))16 .onErrorResume(Exception.class, ex -> handleCircuitBreakerOpen(exchange, ex));17 }18 19 private String getServiceName(ServerHttpRequest request) {20 String path = request.getPath().value();21 if (path.startsWith("/api/users")) return "user-service";22 if (path.startsWith("/api/orders")) return "order-service";23 if (path.startsWith("/api/payments")) return "payment-service";24 return "default-service";25 }26 27 private Mono<Void> handleCircuitBreakerOpen(ServerWebExchange exchange, Exception ex) {28 ServerHttpResponse response = exchange.getResponse();29 response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);30 response.getHeaders().add("Content-Type", "application/json");31 32 String body = "{\"error\":\"Service unavailable\",\"message\":\"Circuit breaker is open\"}";33 DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());34 return response.writeWith(Mono.just(buffer));35 }36 37 @Override38 public int getOrder() {39 return 0;40 }41}3. 监控与日志
3.1 请求日志记录
请求日志过滤器
java
1@Component2@Slf4j3public class RequestLoggingFilter implements GlobalFilter, Ordered {4 5 @Override6 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {7 ServerHttpRequest request = exchange.getRequest();8 long startTime = System.currentTimeMillis();9 10 // 记录请求信息11 String requestId = UUID.randomUUID().toString();12 exchange.getAttributes().put("requestId", requestId);13 exchange.getAttributes().put("startTime", startTime);14 15 log.info("Request started - ID: {}, Method: {}, Path: {}, Client: {}", 16 requestId, request.getMethod(), request.getPath(), 17 getClientIp(request));18 19 return chain.filter(exchange).then(20 Mono.fromRunnable(() -> {21 long endTime = System.currentTimeMillis();22 long duration = endTime - startTime;23 24 ServerHttpResponse response = exchange.getResponse();25 log.info("Request completed - ID: {}, Status: {}, Duration: {}ms", 26 requestId, response.getStatusCode(), duration);27 })28 );29 }30 31 private String getClientIp(ServerHttpRequest request) {32 String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");33 if (xForwardedFor != null && !xForwardedFor.isEmpty()) {34 return xForwardedFor.split(",")[0].trim();35 }36 return request.getRemoteAddress() != null ? 37 request.getRemoteAddress().getAddress().getHostAddress() : "unknown";38 }39 40 @Override41 public int getOrder() {42 return Ordered.HIGHEST_PRECEDENCE;43 }44}4. 配置管理
4.1 动态路由配置
application.yml
yaml
1spring:2 cloud:3 gateway:4 routes:5 - id: user-service6 uri: lb://user-service7 predicates:8 - Path=/api/users/**9 filters:10 - StripPrefix=211 - AddRequestHeader=X-Service-Name, user-service12 - name: RequestRateLimiter13 args:14 redis-rate-limiter.replenishRate: 1015 redis-rate-limiter.burstCapacity: 2016 key-resolver: "#{@userKeyResolver}"17 18 - id: order-service19 uri: lb://order-service20 predicates:21 - Path=/api/orders/**22 filters:23 - StripPrefix=224 - AddRequestHeader=X-Service-Name, order-service25 - name: CircuitBreaker26 args:27 name: order-service-cb28 fallbackUri: forward:/fallback/orders29 30 - id: payment-service31 uri: lb://payment-service32 predicates:33 - Path=/api/payments/**34 filters:35 - StripPrefix=236 - AddRequestHeader=X-Service-Name, payment-service37 - name: Retry38 args:39 retries: 340 statuses: BAD_GATEWAY,GATEWAY_TIMEOUT41 backoff:42 firstBackoff: 10ms43 maxBackoff: 50ms44 factor: 24546 # Redis配置(用于限流)47 redis:48 host: localhost49 port: 637950 timeout: 2000ms51 lettuce:52 pool:53 max-active: 854 max-idle: 855 min-idle: 05657# 熔断器配置58resilience4j:59 circuitbreaker:60 instances:61 user-service-cb:62 slidingWindowSize: 1063 minimumNumberOfCalls: 564 failureRateThreshold: 5065 waitDurationInOpenState: 30s66 permittedNumberOfCallsInHalfOpenState: 367 order-service-cb:68 slidingWindowSize: 2069 minimumNumberOfCalls: 1070 failureRateThreshold: 6071 waitDurationInOpenState: 60s72 payment-service-cb:73 slidingWindowSize: 1574 minimumNumberOfCalls: 875 failureRateThreshold: 4076 waitDurationInOpenState: 45s7778# 监控配置79management:80 endpoints:81 web:82 exposure:83 include: health,info,metrics,prometheus84 endpoint:85 health:86 show-details: always87 metrics:88 export:89 prometheus:90 enabled: true通过本章的学习,你应该已经深入理解了API网关的核心概念、实现方案和最佳实践。API网关是微服务架构的重要基础设施,合理使用网关可以显著提高系统的安全性、可维护性和可扩展性。在实际项目中,要根据业务需求选择合适的网关方案,并注重性能优化和监控运维。
评论