架构之API网关
引言
“API网关是微服务架构的守门人,它不仅是流量的入口,更是服务治理、安全防护、流量控制的核心枢纽。随着服务化在公司内的迅速推进,网关逐步成为应用程序暴露在外网的标准解决方案。”
在微服务架构中,随着服务数量的快速增长,我们面临着一个严峻的挑战:如何让客户端能够方便地访问分散在各个微服务中的功能?如何统一管理外部访问?如何确保系统的安全性和稳定性?API网关作为微服务架构的基础设施,正是解决这些问题的关键组件。
本文将深入探讨API网关的核心价值、设计原则、实现策略以及最佳实践,帮助构建高效、安全、可扩展的微服务入口层。
API网关的核心理念
微服务架构下的接入挑战
在传统的单体应用中,所有的功能都集中在一个应用中,客户端只需要知道一个访问地址即可。但在微服务架构下,情况变得复杂:
API网关的价值定位
API网关作为微服务架构的统一入口,承担着多重角色:
API网关的核心功能
1. 统一接入与路由
智能路由策略
// Spring Cloud Gateway路由配置
@Configuration
public class GatewayRoutingConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 用户服务路由
.route("user-service", r -> r
.path("/api/v1/users/**")
.and().method(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT)
.filters(f -> f
.stripPrefix(2)
.addRequestHeader("X-Service-Id", "user-service")
.circuitBreaker(config -> config
.setName("userServiceCircuitBreaker")
.setFallbackUri("forward:/fallback/user-service"))
.retry(retryConfig -> retryConfig
.setRetries(3)
.setBackoff(Duration.ofMillis(100), Duration.ofMillis(1000), 2, true)))
.uri("lb://user-service"))
// 订单服务路由
.route("order-service", r -> r
.path("/api/v1/orders/**")
.and().header("X-Request-Version", "v2")
.filters(f -> f
.stripPrefix(2)
.requestRateLimiter(rateConfig -> rateConfig
.setRateLimiter(redisRateLimiter())
.setKeyResolver(userKeyResolver()))
.addResponseHeader("X-Response-Version", "v2"))
.uri("lb://order-service"))
// 商品服务路由 - 基于路径变量
.route("product-service", r -> r
.path("/api/v1/products/{category}/**")
.filters(f -> f
.stripPrefix(3)
.addRequestHeader("X-Product-Category", "{category}")
.hystrix(config -> config
.setName("productService")
.setFallbackUri("forward:/fallback/product-service")))
.uri("lb://product-service"))
// 支付服务路由 - 权重负载
.route("payment-service", r -> r
.path("/api/v1/payments/**")
.and().weight("payment-group", 80)
.filters(f -> f.stripPrefix(2))
.uri("lb://payment-service-v1"))
.route("payment-service-v2", r -> r
.path("/api/v1/payments/**")
.and().weight("payment-group", 20)
.filters(f -> f.stripPrefix(2))
.uri("lb://payment-service-v2"))
.build();
}
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(100, 200); // 每秒100个请求,突发200
}
@Bean
public KeyResolver userKeyResolver() {
return exchange -> ReactiveSecurityContextHolder.getContext()
.map(securityContext -> securityContext.getAuthentication().getName())
.switchIfEmpty(Mono.just("anonymous"));
}
}
动态路由管理
// 动态路由服务
@Service
public class DynamicRouteService implements ApplicationEventPublisherAware {
private final RouteDefinitionWriter routeDefinitionWriter;
private final RouteDefinitionLocator routeDefinitionLocator;
private ApplicationEventPublisher publisher;
@Autowired
public DynamicRouteService(RouteDefinitionWriter routeDefinitionWriter,
RouteDefinitionLocator routeDefinitionLocator) {
this.routeDefinitionWriter = routeDefinitionWriter;
this.routeDefinitionLocator = routeDefinitionLocator;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
// 添加新路由
public Mono<String> addRoute(RouteDefinition routeDefinition) {
return routeDefinitionWriter.save(Mono.just(routeDefinition))
.then(Mono.defer(() -> {
publisher.publishEvent(new RefreshRoutesEvent(this));
return Mono.just("Route added successfully");
}));
}
// 更新路由
public Mono<String> updateRoute(RouteDefinition routeDefinition) {
return routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()))
.then(routeDefinitionWriter.save(Mono.just(routeDefinition)))
.then(Mono.defer(() -> {
publisher.publishEvent(new RefreshRoutesEvent(this));
return Mono.just("Route updated successfully");
}));
}
// 删除路由
public Mono<String> deleteRoute(String routeId) {
return routeDefinitionWriter.delete(Mono.just(routeId))
.then(Mono.defer(() -> {
publisher.publishEvent(new RefreshRoutesEvent(this));
return Mono.just("Route deleted successfully");
}));
}
// 获取所有路由
public Flux<RouteDefinition> getAllRoutes() {
return routeDefinitionLocator.getRouteDefinitions();
}
}
2. 安全防护机制
统一认证授权
// JWT认证过滤器
@Component
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {
private final JwtTokenProvider tokenProvider;
private final RedisTemplate<String, String> redisTemplate;
private final AntPathMatcher pathMatcher = new AntPathMatcher();
// 白名单路径
private static final List<String> WHITE_LIST = Arrays.asList(
"/api/v1/auth/login",
"/api/v1/auth/register",
"/api/v1/public/**",
"/actuator/health"
);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 白名单直接放行
if (isWhiteListPath(path)) {
return chain.filter(exchange);
}
// 获取token
String token = extractToken(request);
if (token == null) {
return handleUnauthorized(exchange, "Missing authentication token");
}
try {
// 验证token
if (!tokenProvider.validateToken(token)) {
return handleUnauthorized(exchange, "Invalid authentication token");
}
// 检查token是否被吊销
String jti = tokenProvider.getJti(token);
if (Boolean.TRUE.equals(redisTemplate.hasKey("token:blacklist:" + jti))) {
return handleUnauthorized(exchange, "Token has been revoked");
}
// 解析用户信息
Claims claims = tokenProvider.getClaims(token);
String userId = claims.getSubject();
String role = claims.get("role", String.class);
String permissions = claims.get("permissions", String.class);
// 权限校验
if (!hasPermission(path, request.getMethod().name(), role, permissions)) {
return handleForbidden(exchange, "Insufficient permissions");
}
// 添加用户信息到请求头
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", userId)
.header("X-User-Role", role)
.header("X-User-Permissions", permissions)
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
} catch (Exception e) {
log.error("Authentication error", e);
return handleUnauthorized(exchange, "Authentication failed");
}
}
private boolean isWhiteListPath(String path) {
return WHITE_LIST.stream().anyMatch(pattern -> pathMatcher.match(pattern, path));
}
private String extractToken(ServerHttpRequest request) {
String bearerToken = request.getHeaders().getFirst("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
private boolean hasPermission(String path, String method, String role, String permissions) {
// 权限校验逻辑
return true;
}
private Mono<Void> handleUnauthorized(ServerWebExchange exchange, String message) {
return handleError(exchange, HttpStatus.UNAUTHORIZED, message);
}
private Mono<Void> handleForbidden(ServerWebExchange exchange, String message) {
return handleError(exchange, HttpStatus.FORBIDDEN, message);
}
private Mono<Void> handleError(ServerWebExchange exchange, HttpStatus status, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(status);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("code", status.value());
errorResponse.put("message", message);
errorResponse.put("timestamp", Instant.now().toString());
errorResponse.put("path", exchange.getRequest().getURI().getPath());
byte[] bytes = new ObjectMapper().writeValueAsBytes(errorResponse);
return response.writeWith(Mono.just(response.bufferFactory().wrap(bytes)));
}
@Override
public int getOrder() {
return -100; // 高优先级
}
}
API安全策略
// API安全过滤器
@Component
public class ApiSecurityFilter implements GlobalFilter, Ordered {
private final RedisTemplate<String, String> redisTemplate;
private final RateLimiter rateLimiter;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String clientIp = getClientIp(request);
String path = request.getURI().getPath();
// IP黑名单检查
if (isBlacklistedIp(clientIp)) {
return handleBlocked(exchange, "IP address is blacklisted");
}
// 防SQL注入检查
if (containsSqlInjection(request)) {
logSecurityEvent("SQL_INJECTION", clientIp, path);
return handleBlocked(exchange, "Potential SQL injection detected");
}
// 防XSS攻击检查
if (containsXssAttack(request)) {
logSecurityEvent("XSS_ATTACK", clientIp, path);
return handleBlocked(exchange, "Potential XSS attack detected");
}
// 频率限制检查
if (!rateLimiter.tryAcquire(clientIp)) {
logSecurityEvent("RATE_LIMIT_EXCEEDED", clientIp, path);
return handleRateLimited(exchange);
}
return chain.filter(exchange);
}
private boolean isBlacklistedIp(String ip) {
return Boolean.TRUE.equals(redisTemplate.hasKey("blacklist:ip:" + ip));
}
private boolean containsSqlInjection(ServerHttpRequest request) {
// 检查查询参数
MultiValueMap<String, String> queryParams = request.getQueryParams();
for (List<String> values : queryParams.values()) {
for (String value : values) {
if (isSqlInjectionPattern(value)) {
return true;
}
}
}
// 检查请求头
HttpHeaders headers = request.getHeaders();
for (List<String> headerValues : headers.values()) {
for (String value : headerValues) {
if (isSqlInjectionPattern(value)) {
return true;
}
}
}
return false;
}
private boolean isSqlInjectionPattern(String value) {
if (value == null || value.isEmpty()) {
return false;
}
String lowerValue = value.toLowerCase();
String[] sqlKeywords = {
"select", "insert", "update", "delete", "drop", "union", "where",
"or", "and", "exec", "script", "declare", "cast", "convert"
};
int count = 0;
for (String keyword : sqlKeywords) {
if (lowerValue.contains(keyword)) {
count++;
}
}
return count >= 2; // 包含多个SQL关键字
}
private boolean containsXssAttack(ServerHttpRequest request) {
// 简单的XSS检测逻辑
MultiValueMap<String, String> queryParams = request.getQueryParams();
for (List<String> values : queryParams.values()) {
for (String value : values) {
if (value.contains("<script") || value.contains("javascript:")) {
return true;
}
}
}
return false;
}
private void logSecurityEvent(String eventType, String clientIp, String path) {
SecurityEvent event = SecurityEvent.builder()
.eventType(eventType)
.clientIp(clientIp)
.targetPath(path)
.timestamp(Instant.now())
.build();
redisTemplate.opsForList().rightPush("security:events", event.toJson());
log.warn("Security event detected: {} from {} on {}", eventType, clientIp, path);
}
@Override
public int getOrder() {
return -90; // 高优先级,在认证之前执行
}
}
3. 流量控制与熔断
智能限流策略
// 分布式限流过滤器
@Component
public class DistributedRateLimitFilter implements GlobalFilter, Ordered {
private final RedisTemplate<String, String> redisTemplate;
private final RateLimitRuleService rateLimitRuleService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String clientId = getClientIdentifier(request);
String apiPath = request.getURI().getPath();
String method = request.getMethod().name();
// 获取限流规则
RateLimitRule rule = rateLimitRuleService.getApplicableRule(apiPath, method);
if (rule == null) {
return chain.filter(exchange); // 没有规则,直接放行
}
// 检查是否超限
RateLimitResult result = checkRateLimit(clientId, apiPath, rule);
if (result.isAllowed()) {
// 添加响应头
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("X-RateLimit-Limit", String.valueOf(rule.getLimit()));
response.getHeaders().add("X-RateLimit-Remaining", String.valueOf(result.getRemaining()));
response.getHeaders().add("X-RateLimit-Reset", String.valueOf(result.getResetTime()));
return chain.filter(exchange);
} else {
// 超限处理
return handleRateLimitExceeded(exchange, result);
}
}
private RateLimitResult checkRateLimit(String clientId, String apiPath, RateLimitRule rule) {
String key = buildRateLimitKey(clientId, apiPath, rule);
long currentTime = System.currentTimeMillis();
long windowStart = getWindowStart(currentTime, rule.getWindowSize());
switch (rule.getAlgorithm()) {
case FIXED_WINDOW:
return checkFixedWindow(key, windowStart, rule);
case SLIDING_WINDOW:
return checkSlidingWindow(key, currentTime, rule);
case TOKEN_BUCKET:
return checkTokenBucket(key, rule);
case LEAKY_BUCKET:
return checkLeakyBucket(key, rule);
default:
throw new IllegalArgumentException("Unknown rate limit algorithm: " + rule.getAlgorithm());
}
}
private RateLimitResult checkFixedWindow(String key, long windowStart, RateLimitRule rule) {
String windowKey = key + ":" + windowStart;
String countStr = redisTemplate.opsForValue().get(windowKey);
int count = countStr != null ? Integer.parseInt(countStr) : 0;
if (count < rule.getLimit()) {
// 增加计数
redisTemplate.opsForValue().increment(windowKey);
redisTemplate.expire(windowKey, rule.getWindowSize(), TimeUnit.SECONDS);
return RateLimitResult.allowed(rule.getLimit() - count - 1, windowStart + rule.getWindowSize() * 1000);
} else {
return RateLimitResult.denied(rule.getLimit() - count, windowStart + rule.getWindowSize() * 1000);
}
}
private RateLimitResult checkSlidingWindow(String key, long currentTime, RateLimitRule rule) {
String windowKey = key + ":sliding";
long windowSizeMs = rule.getWindowSize() * 1000;
long windowStart = currentTime - windowSizeMs;
// 清理过期请求记录
redisTemplate.opsForZSet().removeRangeByScore(windowKey, 0, windowStart);
// 获取当前窗口内的请求数
Long currentCount = redisTemplate.opsForZSet().count(windowKey, windowStart, currentTime);
if (currentCount < rule.getLimit()) {
// 记录当前请求
redisTemplate.opsForZSet().add(windowKey, String.valueOf(currentTime), currentTime);
redisTemplate.expire(windowKey, rule.getWindowSize(), TimeUnit.SECONDS);
return RateLimitResult.allowed(rule.getLimit() - currentCount.intValue() - 1, currentTime + windowSizeMs);
} else {
return RateLimitResult.denied(rule.getLimit() - currentCount.intValue(), currentTime + windowSizeMs);
}
}
private RateLimitResult checkTokenBucket(String key, RateLimitRule rule) {
String bucketKey = key + ":bucket";
String tokensStr = redisTemplate.opsForValue().get(bucketKey);
double tokens = tokensStr != null ? Double.parseDouble(tokensStr) : rule.getLimit();
long lastRefillTime = getLastRefillTime(key);
long currentTime = System.currentTimeMillis();
// 计算需要补充的令牌
double tokensToAdd = (currentTime - lastRefillTime) * rule.getLimit() / (rule.getWindowSize() * 1000.0);
tokens = Math.min(rule.getLimit(), tokens + tokensToAdd);
if (tokens >= 1) {
// 消耗一个令牌
tokens -= 1;
redisTemplate.opsForValue().set(bucketKey, String.valueOf(tokens));
setLastRefillTime(key, currentTime);
return RateLimitResult.allowed((int) tokens, currentTime + 1000);
} else {
return RateLimitResult.denied((int) tokens, currentTime + 1000);
}
}
private String buildRateLimitKey(String clientId, String apiPath, RateLimitRule rule) {
StringBuilder keyBuilder = new StringBuilder("ratelimit:");
switch (rule.getScope()) {
case GLOBAL:
keyBuilder.append("global:");
break;
case API:
keyBuilder.append("api:").append(apiPath).append(":");
break;
case USER:
keyBuilder.append("user:").append(clientId).append(":");
break;
case IP:
keyBuilder.append("ip:").append(clientId).append(":");
break;
}
keyBuilder.append(rule.getId());
return keyBuilder.toString();
}
@Override
public int getOrder() {
return -50;
}
}
熔断降级机制
// 熔断器实现
@Component
public class CircuitBreakerFilter implements GatewayFilterFactory<CircuitBreakerFilter.Config> {
private final CircuitBreakerRegistry circuitBreakerRegistry;
private final FallbackHandler fallbackHandler;
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String routeId = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR).getId();
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(routeId);
return circuitBreaker.executeSupplier(() ->
chain.filter(exchange)
.doOnSuccess(aVoid -> recordSuccess(circuitBreaker))
.doOnError(throwable -> recordError(circuitBreaker, throwable))
).onErrorResume(throwable -> {
log.error("Circuit breaker triggered for route: {}", routeId, throwable);
return fallbackHandler.handleFallback(exchange, routeId, throwable);
});
};
}
private void recordSuccess(CircuitBreaker circuitBreaker) {
circuitBreaker.onSuccess(0, TimeUnit.MILLISECONDS);
}
private void recordError(CircuitBreaker circuitBreaker, Throwable throwable) {
circuitBreaker.onError(0, TimeUnit.MILLISECONDS, throwable);
}
@Data
public static class Config {
private String fallbackUri;
private int failureRateThreshold = 50;
private int waitDurationInOpenState = 10000;
private int slidingWindowSize = 100;
private int minimumNumberOfCalls = 10;
}
}
4. 监控与分析
统一监控指标
// 网关监控配置
@Component
public class GatewayMetricsFilter implements GlobalFilter, Ordered {
private final MeterRegistry meterRegistry;
private final Timer.Sample timerSample;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String routeId = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR).getId();
String method = request.getMethod().name();
String path = request.getURI().getPath();
// 记录请求开始时间
long startTime = System.currentTimeMillis();
return chain.filter(exchange)
.doOnSuccess(aVoid -> {
recordMetrics(exchange, routeId, method, path, startTime, null);
})
.doOnError(throwable -> {
recordMetrics(exchange, routeId, method, path, startTime, throwable);
});
}
private void recordMetrics(ServerWebExchange exchange, String routeId, String method,
String path, long startTime, Throwable throwable) {
long duration = System.currentTimeMillis() - startTime;
ServerHttpResponse response = exchange.getResponse();
HttpStatus statusCode = response.getStatusCode();
// 记录响应时间
meterRegistry.timer("gateway.request.duration",
"route", routeId,
"method", method,
"status", String.valueOf(statusCode.value()))
.record(duration, TimeUnit.MILLISECONDS);
// 记录请求数
meterRegistry.counter("gateway.request.count",
"route", routeId,
"method", method,
"status", String.valueOf(statusCode.value()))
.increment();
// 记录错误数
if (throwable != null || (statusCode != null && statusCode.isError())) {
meterRegistry.counter("gateway.request.errors",
"route", routeId,
"method", method,
"status", String.valueOf(statusCode != null ? statusCode.value() : "unknown"),
"error_type", throwable != null ? throwable.getClass().getSimpleName() : "http_error")
.increment();
}
// 记录活跃连接数
meterRegistry.gauge("gateway.active.connections",
Tags.of("route", routeId),
exchange.getRequest().getRemoteAddress() != null ? 1 : 0);
// 记录请求大小
HttpHeaders requestHeaders = exchange.getRequest().getHeaders();
long requestSize = requestHeaders.getContentLength();
if (requestSize > 0) {
meterRegistry.summary("gateway.request.size",
"route", routeId,
"method", method)
.record(requestSize);
}
// 记录响应大小
HttpHeaders responseHeaders = response.getHeaders();
long responseSize = responseHeaders.getContentLength();
if (responseSize > 0) {
meterRegistry.summary("gateway.response.size",
"route", routeId,
"method", method)
.record(responseSize);
}
// 记录自定义业务指标
recordBusinessMetrics(exchange, routeId, method, path);
}
private void recordBusinessMetrics(ServerWebExchange exchange, String routeId,
String method, String path) {
// 用户相关指标
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
if (userId != null) {
meterRegistry.counter("gateway.user.requests",
"user_id", userId,
"route", routeId)
.increment();
}
// API版本指标
String apiVersion = exchange.getRequest().getHeaders().getFirst("X-API-Version");
if (apiVersion != null) {
meterRegistry.counter("gateway.api.version.usage",
"version", apiVersion,
"route", routeId)
.increment();
}
}
@Override
public int getOrder() {
return -10;
}
}
访问日志分析
// 访问日志过滤器
@Component
public class AccessLogFilter implements GlobalFilter, Ordered {
private static final Logger accessLogger = LoggerFactory.getLogger("ACCESS_LOG");
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 构建访问日志
AccessLogEntry logEntry = AccessLogEntry.builder()
.requestId(request.getId())
.timestamp(Instant.now())
.remoteIp(getClientIp(request))
.method(request.getMethod().name())
.uri(request.getURI().toString())
.userAgent(request.getHeaders().getFirst("User-Agent"))
.referer(request.getHeaders().getFirst("Referer"))
.build();
// 记录请求开始
long startTime = System.currentTimeMillis();
return chain.filter(exchange)
.doFinally(signalType -> {
// 记录请求结束
long duration = System.currentTimeMillis() - startTime;
logEntry.setDuration(duration);
logEntry.setStatusCode(exchange.getResponse().getStatusCode().value());
// 记录响应信息
HttpHeaders responseHeaders = exchange.getResponse().getHeaders();
logEntry.setContentType(responseHeaders.getFirst("Content-Type"));
logEntry.setContentLength(responseHeaders.getContentLength());
// 记录用户信息
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
if (userId != null) {
logEntry.setUserId(userId);
}
// 记录路由信息
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
if (route != null) {
logEntry.setRouteId(route.getId());
}
// 异步记录日志
writeAccessLog(logEntry);
});
}
private void writeAccessLog(AccessLogEntry logEntry) {
try {
String logLine = objectMapper.writeValueAsString(logEntry);
accessLogger.info(logLine);
} catch (Exception e) {
accessLogger.error("Failed to write access log", e);
}
}
private String getClientIp(ServerHttpRequest request) {
HttpHeaders headers = request.getHeaders();
String ip = headers.getFirst("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = headers.getFirst("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = headers.getFirst("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddress().getAddress().getHostAddress();
}
// 如果存在多个IP,取第一个
if (ip != null && ip.contains(",")) {
ip = ip.substring(0, ip.indexOf(",")).trim();
}
return ip;
}
@Override
public int getOrder() {
return -5;
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class AccessLogEntry {
private String requestId;
private Instant timestamp;
private String remoteIp;
private String method;
private String uri;
private String userAgent;
private String referer;
private String userId;
private String routeId;
private long duration;
private int statusCode;
private String contentType;
private long contentLength;
}
API网关的高可用设计
1. 集群部署架构
# Kubernetes部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
namespace: infrastructure
spec:
replicas: 3
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- api-gateway
topologyKey: kubernetes.io/hostname
containers:
- name: api-gateway
image: api-gateway:latest
ports:
- containerPort: 8080
name: http
- containerPort: 8081
name: admin
env:
- name: SPRING_PROFILES_ACTIVE
value: "production"
- name: REDIS_HOST
value: "redis-cluster"
- name: EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE
value: "http://eureka1:8761/eureka,http://eureka2:8761/eureka,http://eureka3:8761/eureka"
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: admin
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: admin
initialDelaySeconds: 30
periodSeconds: 10
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
volumes:
- name: config
configMap:
name: api-gateway-config
---
apiVersion: v1
kind: Service
metadata:
name: api-gateway-service
namespace: infrastructure
spec:
selector:
app: api-gateway
ports:
- name: http
port: 80
targetPort: 8080
- name: admin
port: 8081
targetPort: 8081
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: api-gateway-loadbalancer
namespace: infrastructure
spec:
selector:
app: api-gateway
ports:
- name: http
port: 80
targetPort: 8080
type: LoadBalancer
2. 配置中心集成
// 配置中心客户端
@Component
@ConfigurationProperties(prefix = "gateway")
@Data
public class GatewayProperties {
private Security security = new Security();
private RateLimit rateLimit = new RateLimit();
private CircuitBreaker circuitBreaker = new CircuitBreaker();
private Monitoring monitoring = new Monitoring();
@Data
public static class Security {
private boolean enabled = true;
private Jwt jwt = new Jwt();
private List<String> whiteList = new ArrayList<>();
private List<String> blackList = new ArrayList<>();
@Data
public static class Jwt {
private String secret;
private long expiration = 3600000; // 1 hour
private String header = "Authorization";
private String prefix = "Bearer ";
}
}
@Data
public static class RateLimit {
private boolean enabled = true;
private int defaultLimit = 100;
private int defaultWindow = 60; // seconds
private Redis redis = new Redis();
@Data
public static class Redis {
private String host = "localhost";
private int port = 6379;
private String password;
private int database = 0;
}
}
@Data
public static class CircuitBreaker {
private boolean enabled = true;
private int failureRateThreshold = 50;
private int waitDurationInOpenState = 10000; // milliseconds
private int slidingWindowSize = 100;
private int minimumNumberOfCalls = 10;
}
@Data
public static class Monitoring {
private boolean enabled = true;
private Metrics metrics = new Metrics();
private Logging logging = new Logging();
@Data
public static class Metrics {
private boolean enabled = true;
private List<String> exportEndpoints = Arrays.asList("prometheus", "influxdb");
}
@Data
public static class Logging {
private boolean enabled = true;
private String level = "INFO";
private boolean structured = true;
}
}
}
API网关的最佳实践
1. 设计原则
// API设计规范
@RestController
@RequestMapping("/api/v1")
public class ApiDesignController {
// RESTful API设计
@GetMapping("/users/{userId}")
public ResponseEntity<UserDTO> getUser(@PathVariable String userId) {
// 1. 使用名词复数形式
// 2. 使用HTTP方法表示操作
// 3. 使用状态码表示结果
return ResponseEntity.ok(userService.getUser(userId));
}
@PostMapping("/users")
public ResponseEntity<UserDTO> createUser(@Valid @RequestBody CreateUserRequest request) {
UserDTO user = userService.createUser(request);
// 返回创建资源的URI
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{userId}")
.buildAndExpand(user.getUserId())
.toUri();
return ResponseEntity.created(location).body(user);
}
@PutMapping("/users/{userId}")
public ResponseEntity<UserDTO> updateUser(@PathVariable String userId,
@Valid @RequestBody UpdateUserRequest request) {
UserDTO user = userService.updateUser(userId, request);
return ResponseEntity.ok(user);
}
@DeleteMapping("/users/{userId}")
public ResponseEntity<Void> deleteUser(@PathVariable String userId) {
userService.deleteUser(userId);
return ResponseEntity.noContent().build();
}
// 批量操作
@PostMapping("/users/batch")
public ResponseEntity<BatchOperationResult> batchCreateUsers(
@Valid @RequestBody List<CreateUserRequest> requests) {
BatchOperationResult result = userService.batchCreateUsers(requests);
return ResponseEntity.ok(result);
}
// 分页查询
@GetMapping("/users")
public ResponseEntity<PageResult<UserDTO>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false) String sort,
@RequestParam(required = false) String filter) {
PageRequest pageRequest = PageRequest.of(page, size, Sort.by(sort));
PageResult<UserDTO> users = userService.getUsers(pageRequest, filter);
return ResponseEntity.ok(users);
}
}
2. 版本管理策略
// API版本管理
@Component
public class ApiVersionManager {
private final Map<String, ApiVersionInfo> versionMap = new ConcurrentHashMap<>();
public void registerVersion(String apiPath, String version, Date deprecationDate, String migrationGuide) {
ApiVersionInfo versionInfo = ApiVersionInfo.builder()
.apiPath(apiPath)
.version(version)
.releaseDate(new Date())
.deprecationDate(deprecationDate)
.migrationGuide(migrationGuide)
.status(ApiStatus.ACTIVE)
.build();
versionMap.put(apiPath + ":" + version, versionInfo);
}
public ApiVersionInfo getVersionInfo(String apiPath, String version) {
return versionMap.get(apiPath + ":" + version);
}
public List<ApiVersionInfo> getAllVersions(String apiPath) {
return versionMap.entrySet().stream()
.filter(entry -> entry.getKey().startsWith(apiPath + ":"))
.map(Map.Entry::getValue)
.sorted(Comparator.comparing(ApiVersionInfo::getVersion).reversed())
.collect(Collectors.toList());
}
public boolean isVersionDeprecated(String apiPath, String version) {
ApiVersionInfo versionInfo = getVersionInfo(apiPath, version);
if (versionInfo == null) {
return false;
}
Date deprecationDate = versionInfo.getDeprecationDate();
return deprecationDate != null && new Date().after(deprecationDate);
}
public String getRecommendedVersion(String apiPath) {
return getAllVersions(apiPath).stream()
.filter(v -> v.getStatus() == ApiStatus.ACTIVE)
.findFirst()
.map(ApiVersionInfo::getVersion)
.orElse(null);
}
}
// 版本过滤器
@Component
public class ApiVersionFilter implements GlobalFilter, Ordered {
private final ApiVersionManager versionManager;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 提取版本信息
String version = extractVersion(request);
if (version == null) {
// 没有指定版本,使用最新版本
version = versionManager.getRecommendedVersion(path);
if (version != null) {
// 添加版本到请求头
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-API-Version", version)
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
} else {
// 检查版本是否已废弃
if (versionManager.isVersionDeprecated(path, version)) {
// 添加废弃警告头
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("X-API-Deprecated", "true");
response.getHeaders().add("X-API-Deprecation-Date",
versionManager.getVersionInfo(path, version).getDeprecationDate().toString());
String recommendedVersion = versionManager.getRecommendedVersion(path);
if (recommendedVersion != null) {
response.getHeaders().add("X-API-Recommended-Version", recommendedVersion);
}
}
}
return chain.filter(exchange);
}
private String extractVersion(ServerHttpRequest request) {
// 从请求头中提取版本
String version = request.getHeaders().getFirst("X-API-Version");
if (version != null) {
return version;
}
// 从URL路径中提取版本
String path = request.getURI().getPath();
if (path.contains("/v1/")) {
return "v1";
} else if (path.contains("/v2/")) {
return "v2";
}
return null;
}
@Override
public int getOrder() {
return -20;
}
}
3. 性能优化策略
// 响应缓存配置
@Configuration
@EnableCaching
public class ResponseCacheConfig {
@Bean
public CacheManager gatewayCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.recordStats());
return cacheManager;
}
@Bean
public CacheManager apiCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(5000)
.expireAfterWrite(1, TimeUnit.MINUTES)
.recordStats());
return cacheManager;
}
}
// 缓存过滤器
@Component
public class ResponseCacheFilter implements GlobalFilter, Ordered {
private final CacheManager cacheManager;
private final CacheKeyGenerator cacheKeyGenerator;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String method = request.getMethod().name();
String path = request.getURI().getPath();
// 只缓存GET请求
if (!"GET".equals(method)) {
return chain.filter(exchange);
}
// 生成缓存key
String cacheKey = cacheKeyGenerator.generateKey(request);
String cacheName = getCacheName(path);
// 检查缓存
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
CachedResponse cachedResponse = cache.get(cacheKey, CachedResponse.class);
if (cachedResponse != null && !cachedResponse.isExpired()) {
// 返回缓存响应
return writeCachedResponse(exchange, cachedResponse);
}
}
// 继续处理请求
return chain.filter(exchange)
.then(Mono.defer(() -> {
// 缓存响应
return cacheResponse(exchange, cacheKey, cacheName);
}));
}
private Mono<Void> cacheResponse(ServerWebExchange exchange, String cacheKey, String cacheName) {
ServerHttpResponse response = exchange.getResponse();
HttpStatusCode statusCode = response.getStatusCode();
// 只缓存成功的响应
if (statusCode != null && statusCode.is2xxSuccessful()) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
// 获取响应内容
DataBufferFactory bufferFactory = response.bufferFactory();
// 这里需要实现响应内容的捕获和缓存逻辑
// 简化示例
CachedResponse cachedResponse = CachedResponse.builder()
.statusCode(statusCode.value())
.headers(response.getHeaders())
.body(new byte[0]) // 实际应该包含响应体
.expireTime(System.currentTimeMillis() + getCacheTTL(path))
.build();
cache.put(cacheKey, cachedResponse);
}
}
return Mono.empty();
}
private String getCacheName(String path) {
if (path.startsWith("/api/v1/users")) {
return "user-api-cache";
} else if (path.startsWith("/api/v1/products")) {
return "product-api-cache";
} else {
return "default-api-cache";
}
}
private long getCacheTTL(String path) {
if (path.startsWith("/api/v1/users")) {
return TimeUnit.MINUTES.toMillis(5);
} else if (path.startsWith("/api/v1/products")) {
return TimeUnit.MINUTES.toMillis(10);
} else {
return TimeUnit.MINUTES.toMillis(1);
}
}
@Override
public int getOrder() {
return 10;
}
}
API网关的演进趋势
1. 云原生网关
# Istio服务网格配置
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: api-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- api.example.com
tls:
httpsRedirect: true
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: api-gateway-cert
hosts:
- api.example.com
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api-routes
namespace: default
spec:
hosts:
- api.example.com
gateways:
- istio-system/api-gateway
http:
- match:
- uri:
prefix: /api/v1/users
route:
- destination:
host: user-service
port:
number: 8080
weight: 100
- destination:
host: user-service-v2
port:
number: 8080
weight: 0
fault:
delay:
percentage:
value: 0.1
fixedDelay: 5s
retries:
attempts: 3
perTryTimeout: 2s
retryOn: 5xx,reset,connect-failure,refused-stream
timeout: 10s
corsPolicy:
allowOrigins:
- exact: https://app.example.com
allowMethods:
- GET
- POST
- PUT
- DELETE
allowHeaders:
- authorization
- content-type
- x-request-id
maxAge: 24h
2. GraphQL网关
// GraphQL网关配置
@Configuration
public class GraphQLGatewayConfig {
@Bean
public GraphQLSchema graphQLSchema() {
return Federation.transform(
GraphQLSchema.newSchema()
.query(GraphQLObjectType.newObject()
.name("Query")
.field(GraphQLFieldDefinition.newFieldDefinition()
.name("user")
.type(GraphQLTypeReference.typeRef("User"))
.argument(GraphQLArgument.newArgument()
.name("id")
.type(Scalars.GraphQLString)
.build())
.dataFetcher(environment -> {
String userId = environment.getArgument("id");
return userService.getUser(userId);
})
.build())
.build())
.build()
).build();
}
@Bean
public GraphQL graphQL(GraphQLSchema schema) {
return GraphQL.newGraphQL(schema)
.instrumentation(new TracingInstrumentation())
.queryExecutionStrategy(new AsyncExecutionStrategy())
.mutationExecutionStrategy(new AsyncSerialExecutionStrategy())
.build();
}
}
// GraphQL控制器
@RestController
@RequestMapping("/graphql")
public class GraphQLController {
private final GraphQL graphQL;
private final ObjectMapper objectMapper;
@PostMapping
public Mono<ResponseEntity<Map<String, Object>>> graphql(
@RequestBody Map<String, Object> request,
@RequestHeader Map<String, String> headers) {
String query = (String) request.get("query");
String operationName = (String) request.get("operationName");
Map<String, Object> variables = (Map<String, Object>) request.get("variables");
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.query(query)
.operationName(operationName)
.variables(variables != null ? variables : Collections.emptyMap())
.context(headers)
.build();
return Mono.fromCallable(() -> graphQL.execute(executionInput))
.subscribeOn(Schedulers.boundedElastic())
.map(executionResult -> {
Map<String, Object> response = new LinkedHashMap<>();
response.put("data", executionResult.getData());
if (!executionResult.getErrors().isEmpty()) {
response.put("errors", executionResult.getErrors().stream()
.map(error -> Map.of(
"message", error.getMessage(),
"locations", error.getLocations(),
"path", error.getPath()
))
.collect(Collectors.toList()));
}
return ResponseEntity.ok(response);
});
}
}
总结
API网关作为微服务架构的核心基础设施,承担着统一接入、安全防护、流量控制、服务治理等多重职责。通过遵循API网关法则,我们能够:
核心价值
- 统一服务入口:为所有微服务提供统一的访问入口,简化客户端调用
- 增强安全防护:集中处理认证授权、数据加密、攻击防护等安全问题
- 智能流量控制:通过限流、熔断、负载均衡等机制保障系统稳定性
- 简化服务治理:统一处理服务发现、健康检查、灰度发布等治理需求
- 提升可观测性:集中收集访问日志、性能指标、错误统计等监控数据
关键原则
- 单一职责:每个过滤器只负责特定的功能,保持职责单一
- 可配置性:支持动态配置路由规则、安全策略、限流参数等
- 高可用性:通过集群部署、故障转移、降级策略确保服务可用
- 性能优先:通过缓存、异步处理、连接池等技术优化性能
- 可扩展性:支持自定义过滤器、插件机制,便于功能扩展
成功要素
- 合理的路由设计:基于业务域设计清晰的路由规则
- 完善的安全机制:建立多层次的安全防护体系
- 智能的流量管理:根据业务特点制定合适的限流熔断策略
- 全面的监控覆盖:建立从基础设施到业务指标的完整监控体系
- 持续的性能优化:通过缓存、压缩、连接复用等技术提升性能
记住:API网关是微服务架构的守门人,它不仅决定了外部如何访问内部服务,更直接影响着整个系统的安全性、稳定性和性能表现。通过遵循API网关法则,我们能够构建出既安全又高效、既稳定又可扩展的优秀微服务架构。
API网关法则提醒我们:在微服务架构中,统一的服务入口层是不可或缺的。只有通过API网关的统一管理,我们才能真正发挥微服务架构的优势,同时避免分布式系统带来的复杂性。通过遵循这一原则,我们能够构建出既满足当前需求,又具备未来扩展性的优秀架构。
1016

被折叠的 条评论
为什么被折叠?



