API网关在微服务架构中承担统一入口、请求路由、限流熔断等核心职责,是后端开发中服务治理的关键基础设施。相比客户端直连各微服务,API网关屏蔽了内部服务拓扑,提供统一的鉴权、监控和流量控制能力,在高并发设计和业务中台建设中扮演枢纽角色。
API网关核心功能与技术选型
主流API网关方案包括Spring Cloud Gateway(Java生态)、Kong(Lua/Nginx)、APISIX(Lua/Nginx)和Envoy(C++)。Spring Cloud Gateway基于WebFlux响应式模型,与Spring Boot生态无缝集成,适合Java技术栈团队:
// Spring Cloud Gateway 基础配置
// application.yml
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=2
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=2
lb://前缀表示使用负载均衡发现服务实例,StripPrefix=2去掉URL前两级路径(/api/users -> /)。RequestRateLimiter基于Redis实现令牌桶限流,replenishRate为令牌填充速率(100/秒),burstCapacity为桶容量(200)。
限流策略设计与多维度限流实现
单一维度的限流无法满足复杂业务场景。API网关需要支持按API、按用户、按IP等多维度限流。通过自定义GatewayFilter实现多维度限流:
@Component
public class MultiDimensionRateLimiterFilter implements GlobalFilter, Ordered {
@Autowired
private StringRedisTemplate redis;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String apiPath = exchange.getRequest().getPath().value();
String clientIp = getClientIp(exchange);
String userId = getUserId(exchange);
// API级别限流: 每个API 1000 QPS
if (!tryAcquire("rate:api:" + apiPath, 1000, 1)) {
return rateLimitResponse(exchange, "API限流");
}
// 用户级别限流: 每个用户 100 QPS
if (userId != null && !tryAcquire("rate:user:" + userId, 100, 1)) {
return rateLimitResponse(exchange, "用户限流");
}
// IP级别限流: 防刷
if (!tryAcquire("rate:ip:" + clientIp, 50, 1)) {
return rateLimitResponse(exchange, "IP限流");
}
return chain.filter(exchange);
}
private boolean tryAcquire(String key, int limit, int windowSec) {
// Redis滑动窗口限流
long now = System.currentTimeMillis();
long windowStart = now - windowSec * 1000L;
redis.opsForZSet().removeRangeByScore(key, 0, windowStart);
Long count = redis.opsForZSet().zCard(key);
if (count != null && count >= limit) {
return false;
}
redis.opsForZSet().add(key, now + ":" + Math.random(), now);
redis.expire(key, windowSec + 1, TimeUnit.SECONDS);
return true;
}
@Override
public int getOrder() { return -100; }
}
滑动窗口限流比固定窗口更精确,避免了窗口边界处的突发流量问题。Redis ZSet的score存储时间戳,通过ZREMRANGEBYSCORE清理过期记录,ZCARD统计窗口内请求数。这种方案在Redis单节点下可支撑10万QPS级别的限流判断。
熔断降级机制与Sentinel集成
限流保护网关自身,熔断保护下游服务。当某个下游服务持续超时或报错时,熔断器打开,快速失败而非等待超时,防止故障扩散。集成Sentinel实现熔断降级:
// 自定义熔断降级过滤器
@Component
public class CircuitBreakerFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String serviceId = getServiceId(exchange);
return chain.filter(exchange)
.timeout(Duration.ofSeconds(3)) // 超时3秒
.onErrorResume(ex -> {
if (ex instanceof TimeoutException) {
// 记录超时次数,达到阈值触发熔断
circuitBreakerRegistry.recordFailure(serviceId);
return fallbackResponse(exchange, "服务超时,请稍后重试");
}
return Mono.error(ex);
});
}
}
// 熔断器状态管理
@Service
public class CircuitBreakerRegistry {
private Map<String, CircuitBreakerState> breakers = new ConcurrentHashMap<>();
public boolean isAllowed(String serviceId) {
CircuitBreakerState state = breakers.computeIfAbsent(
serviceId, k -> new CircuitBreakerState()
);
return state.allowRequest();
}
public void recordFailure(String serviceId) {
CircuitBreakerState state = breakers.get(serviceId);
if (state != null) state.recordFailure();
}
}
class CircuitBreakerState {
private AtomicInteger failures = new AtomicInteger(0);
private volatile long lastFailureTime = 0;
private static final int THRESHOLD = 5;
private static final long RECOVERY_TIMEOUT = 30000; // 30秒半开尝试
boolean allowRequest() {
if (failures.get() < THRESHOLD) return true;
// 熔断状态,检查是否到半开时间
if (System.currentTimeMillis() - lastFailureTime > RECOVERY_TIMEOUT) {
failures.set(0); // 重置,进入半开
return true;
}
return false;
}
void recordFailure() {
failures.incrementAndGet();
lastFailureTime = System.currentTimeMillis();
}
}
熔断器有三种状态:Closed(正常放行)、Open(直接拒绝)、Half-Open(试探性放行)。当连续失败达到阈值(5次)时进入Open状态,30秒后进入Half-Open状态放行一个请求试探,成功则恢复Closed,失败则重新Open。
动态路由与灰度发布实现
动态路由允许不重启网关即可调整路由规则,是灰度发布的基础。通过Nacos/Apollo配置中心存储路由规则,网关监听配置变更动态刷新:
// 动态路由监听
@Component
public class DynamicRouteListener implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionLocator routeDefinitionLocator;
@NacosConfigListener(dataId = "gateway-routes.json")
public void onRouteChange(String config) {
List<RouteDefinition> routes = JSON.parseArray(config, RouteDefinition.class);
// 清除旧路由
routeDefinitionLocator.getRouteDefinitions().collectList()
.subscribe(oldRoutes -> {
oldRoutes.forEach(route ->
publisher.publishEvent(new RefreshRoutesEvent(this))
);
});
// 加载新路由
routes.forEach(route ->
publisher.publishEvent(new RefreshRoutesEvent(this))
);
}
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
}
灰度发布通过路由权重控制流量分配。10%流量到新版本,90%到旧版本,逐步增加新版本权重直到全量切换。结合Header标记或Cookie标记实现按用户灰度:特定用户群体先体验新功能,验证无问题后扩展到全部用户。API网关作为流量控制中枢,配合服务注册中心和配置中心,实现了微服务治理的核心闭环。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/api-wang-guan-jia-gou-she-ji-shi-zhan-xian-liu-rong-duan-yu/