微服务网关设计与实现
前言
在微服务架构中,API网关是不可或缺的组件。它作为系统的入口,承担着路由转发、安全认证、流量控制等关键职责。本文将深入探讨API网关的设计理念和实现方案,帮助你构建高性能、高可用的网关系统。
一、为什么需要API网关
1.1 微服务面临的挑战
问题1:客户端复杂性
1 2 3 4
| 客户端 → 用户服务 (http://user-service:8001) → 订单服务 (http://order-service:8002) → 商品服务 (http://product-service:8003) → 支付服务 (http://payment-service:8004)
|
客户端需要:
- 维护多个服务地址
- 处理不同的协议
- 实现重试和容错逻辑
- 管理认证凭证
问题2:横切关注点
- 认证授权:每个服务都要实现?
- 限流熔断:如何统一管理?
- 日志监控:分散在各个服务?
- 协议转换:HTTP、gRPC、WebSocket?
1.2 API网关的作用
1 2 3 4 5 6 7 8
| 客户端 → [API网关] → 微服务集群 ↓ • 路由转发 • 认证授权 • 限流熔断 • 负载均衡 • 协议转换 • 日志监控
|
核心价值:
- 简化客户端:单一入口
- 集中管理:统一的横切关注点
- 解耦服务:服务位置透明
- 提升安全:统一认证鉴权
二、网关核心功能
2.1 路由转发
基于路径路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| routes: - id: user-service uri: lb://user-service predicates: - Path=/api/users/** filters: - StripPrefix=1 - id: order-service uri: lb://order-service predicates: - Path=/api/orders/** filters: - StripPrefix=1
|
基于请求头路由:
1 2 3 4 5 6 7 8 9 10
| routes: - id: api-v1 uri: http://api-v1.example.com predicates: - Header=X-API-Version, v1 - id: api-v2 uri: http://api-v2.example.com predicates: - Header=X-API-Version, v2
|
基于参数路由:
1 2 3 4 5
| routes: - id: beta-users uri: http://beta.example.com predicates: - Query=beta, true
|
2.2 认证授权
JWT认证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @Component public class JwtAuthenticationFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); if (isWhiteList(request.getPath().value())) { return chain.filter(exchange); } String token = request.getHeaders().getFirst("Authorization"); if (token == null || !token.startsWith("Bearer ")) { return unauthorized(exchange); } try { Claims claims = JwtUtil.parseToken(token.substring(7)); ServerHttpRequest mutated = request.mutate() .header("X-User-Id", claims.get("userId", String.class)) .header("X-User-Name", claims.get("username", String.class)) .build(); return chain.filter(exchange.mutate().request(mutated).build()); } catch (Exception e) { return unauthorized(exchange); } } private Mono<Void> unauthorized(ServerWebExchange exchange) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } @Override public int getOrder() { return -100; } }
|
2.3 限流
令牌桶算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Component public class RateLimitFilter implements GlobalFilter, Ordered { private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>(); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String clientId = getClientId(exchange); RateLimiter limiter = limiters.computeIfAbsent( clientId, k -> RateLimiter.create(10.0) ); if (!limiter.tryAcquire()) { exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } private String getClientId(ServerWebExchange exchange) { return exchange.getRequest().getRemoteAddress().getAddress().getHostAddress(); } @Override public int getOrder() { return -50; } }
|
Redis分布式限流:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Component public class RedisRateLimitFilter implements GlobalFilter, Ordered { @Autowired private StringRedisTemplate redisTemplate; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String key = "rate_limit:" + getClientId(exchange); return Mono.fromCallable(() -> { Long count = redisTemplate.opsForValue().increment(key); if (count == 1) { redisTemplate.expire(key, 1, TimeUnit.SECONDS); } return count; }) .flatMap(count -> { if (count > 10) { exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); return exchange.getResponse().setComplete(); } return chain.filter(exchange); }); } @Override public int getOrder() { return -50; } }
|
2.4 熔断降级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| @Component public class CircuitBreakerFilter implements GlobalFilter, Ordered { private final CircuitBreakerRegistry circuitBreakerRegistry; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String serviceName = getServiceName(exchange); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(serviceName); return Mono.defer(() -> chain.filter(exchange)) .transformDeferred(CircuitBreakerOperator.of(circuitBreaker)) .onErrorResume(throwable -> { return fallbackResponse(exchange, throwable); }); } private Mono<Void> fallbackResponse(ServerWebExchange exchange, Throwable throwable) { exchange.getResponse().setStatusCode(HttpStatus.SERVICE_UNAVAILABLE); String fallbackBody = """ { "error": "Service temporarily unavailable", "message": "Please try again later" } """; DataBuffer buffer = exchange.getResponse() .bufferFactory() .wrap(fallbackBody.getBytes(StandardCharsets.UTF_8)); return exchange.getResponse().writeWith(Mono.just(buffer)); } @Override public int getOrder() { return 0; } }
|
三、Spring Cloud Gateway实战
3.1 项目搭建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <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-data-redis-reactive</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId> </dependency> </dependencies>
|
3.2 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| server: port: 8080
spring: application: name: api-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: discovery: locator: enabled: true lower-case-service-id: true routes: - id: user-service uri: lb://user-service predicates: - Path=/api/users/** filters: - StripPrefix=1 - name: CircuitBreaker args: name: userServiceCircuitBreaker fallbackUri: forward:/fallback/user - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 key-resolver: "#{@ipKeyResolver}" - id: order-service uri: lb://order-service predicates: - Path=/api/orders/** filters: - StripPrefix=1 - name: Retry args: retries: 3 statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE methods: GET backoff: firstBackoff: 10ms maxBackoff: 50ms factor: 2 default-filters: - AddResponseHeader=X-Response-Time, ${response-time} - AddResponseHeader=X-Gateway-Version, 1.0 redis: host: localhost port: 6379
resilience4j: circuitbreaker: configs: default: slidingWindowSize: 100 permittedNumberOfCallsInHalfOpenState: 10 waitDurationInOpenState: 10000 failureRateThreshold: 50 slowCallRateThreshold: 50 slowCallDurationThreshold: 2000
|
3.3 自定义过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| @Component public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> { public CustomGatewayFilterFactory() { super(Config.class); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); exchange.getAttributes().put("startTime", System.currentTimeMillis()); ServerHttpRequest mutated = request.mutate() .header("X-Custom-Header", config.getValue()) .build(); return chain.filter(exchange.mutate().request(mutated).build()) .then(Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute("startTime"); if (startTime != null) { long duration = System.currentTimeMillis() - startTime; log.info("Request to {} took {}ms", request.getURI(), duration); } })); }; } public static class Config { private String value; } }
|
四、性能优化
4.1 连接池优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: cloud: gateway: httpclient: connect-timeout: 1000 response-timeout: 5s pool: type: ELASTIC max-connections: 1000 max-idle-time: 30s max-life-time: 60s
|
4.2 缓存优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| @Component public class CacheFilter implements GlobalFilter, Ordered { @Autowired private StringRedisTemplate redisTemplate; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); if (!request.getMethod().equals(HttpMethod.GET)) { return chain.filter(exchange); } String cacheKey = generateCacheKey(request); return Mono.fromCallable(() -> redisTemplate.opsForValue().get(cacheKey)) .flatMap(cachedResponse -> { if (cachedResponse != null) { return writeCachedResponse(exchange, cachedResponse); } return cacheResponse(exchange, chain, cacheKey); }); } private Mono<Void> cacheResponse(ServerWebExchange exchange, GatewayFilterChain chain, String cacheKey) { ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (body instanceof Flux) { return super.writeWith( Flux.from(body).buffer().map(dataBuffers -> { DataBuffer joinedBuffer = bufferFactory.join(dataBuffers); byte[] content = new byte[joinedBuffer.readableByteCount()]; joinedBuffer.read(content); redisTemplate.opsForValue().set( cacheKey, new String(content, StandardCharsets.UTF_8), 5, TimeUnit.MINUTES ); return bufferFactory.wrap(content); }) ); } return super.writeWith(body); } }; return chain.filter(exchange.mutate().response(decoratedResponse).build()); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE + 10; } }
|
4.3 并发优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("parallel-requests", r -> r .path("/api/dashboard") .filters(f -> f.filter(new ParallelRequestFilter())) .uri("no://op")) .build(); } }
public class ParallelRequestFilter implements GatewayFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { Mono<UserResponse> userMono = callUserService(); Mono<OrderResponse> orderMono = callOrderService(); Mono<ProductResponse> productMono = callProductService(); return Mono.zip(userMono, orderMono, productMono) .flatMap(tuple -> { DashboardResponse response = new DashboardResponse( tuple.getT1(), tuple.getT2(), tuple.getT3() ); return writeResponse(exchange, response); }); } }
|
五、监控与可观测性
5.1 Metrics集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| @Configuration public class MetricsConfig { @Bean public MetricsFilter metricsFilter(MeterRegistry meterRegistry) { return new MetricsFilter(meterRegistry); } }
@Component public class MetricsFilter implements GlobalFilter, Ordered { private final MeterRegistry meterRegistry; private final Counter requestCounter; private final Timer requestTimer; public MetricsFilter(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; this.requestCounter = Counter.builder("gateway.requests") .description("Total gateway requests") .register(meterRegistry); this.requestTimer = Timer.builder("gateway.request.duration") .description("Gateway request duration") .register(meterRegistry); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { Timer.Sample sample = Timer.start(meterRegistry); return chain.filter(exchange) .doFinally(signalType -> { requestCounter.increment(); sample.stop(requestTimer); String route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); if (route != null) { meterRegistry.counter("gateway.requests", "route", route).increment(); } }); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
|
5.2 日志追踪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Component public class TraceFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String traceId = UUID.randomUUID().toString(); ServerHttpRequest mutated = exchange.getRequest().mutate() .header("X-Trace-Id", traceId) .build(); exchange.getResponse().getHeaders().add("X-Trace-Id", traceId); log.info("[{}] {} {} from {}", traceId, exchange.getRequest().getMethod(), exchange.getRequest().getURI(), exchange.getRequest().getRemoteAddress() ); return chain.filter(exchange.mutate().request(mutated).build()) .doFinally(signalType -> { log.info("[{}] Response status: {}", traceId, exchange.getResponse().getStatusCode() ); }); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
|
六、高可用架构
6.1 集群部署
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| apiVersion: apps/v1 kind: Deployment metadata: name: api-gateway spec: replicas: 3 selector: matchLabels: app: api-gateway template: metadata: labels: app: api-gateway spec: containers: - name: gateway image: api-gateway:1.0 ports: - containerPort: 8080 resources: requests: memory: "512Mi" cpu: "500m" limits: memory: "1Gi" cpu: "1000m" livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 10 periodSeconds: 5
--- apiVersion: v1 kind: Service metadata: name: api-gateway spec: selector: app: api-gateway ports: - port: 80 targetPort: 8080 type: LoadBalancer
|
6.2 灰度发布
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Component public class GrayReleaseFilter implements GlobalFilter, Ordered { @Value("${gray.version}") private String grayVersion; @Value("${gray.traffic.ratio}") private double grayTrafficRatio; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id"); boolean isGrayUser = checkGrayUser(userId); boolean isGrayTraffic = Math.random() < grayTrafficRatio; if (isGrayUser || isGrayTraffic) { ServerHttpRequest mutated = exchange.getRequest().mutate() .header("X-Service-Version", grayVersion) .build(); return chain.filter(exchange.mutate().request(mutated).build()); } return chain.filter(exchange); } @Override public int getOrder() { return -200; } }
|
七、最佳实践
7.1 设计原则
- 单一职责:网关只做路由和策略,不处理业务逻辑
- 高性能:异步非阻塞,减少延迟
- 高可用:集群部署,无状态设计
- 可扩展:插件化架构,易于定制
7.2 安全建议
- 启用HTTPS
- 实施速率限制
- 验证输入数据
- 防止SQL注入和XSS
- 定期更新依赖
7.3 监控指标
- QPS/TPS
- 响应时间(P50/P95/P99)
- 错误率
- 熔断次数
- 限流次数
八、总结
API网关是微服务架构的关键组件,合理的设计和实现可以:
- 简化客户端:统一入口
- 提升安全:集中认证鉴权
- 保障稳定:限流熔断
- 优化性能:缓存、并行请求
掌握这些技能,你就能构建高性能的微服务网关系统!
参考资料