基于Spring Cloud Gateway的外卖霸王餐API限流熔断与灰度发布实践
背景:外卖霸王餐业务的高并发挑战
外卖霸王餐活动瞬时流量可达平日20倍,传统网关难以支撑。本文基于Spring Cloud Gateway构建弹性网关,实现限流、熔断、灰度三位一体防护体系,核心代码,可直接落地生产。
一、架构总览:三层防线
用户请求 → Spring Cloud Gateway → 限流层(Redis令牌桶)→ 熔断层(Resilience4j)→ 灰度层(Ribbon+Metadata)→ 下游微服务
所有过滤器均注册到cn.juwatech.gateway.filter包,统一配置中心采用Nacos,动态刷新秒级生效。

二、限流:Redis令牌桶精准控速
目标:单接口QPS不超过3000,超出直接返回429。核心实现:
package cn.juwatech.gateway.filter;
public class RateLimitFilter extends AbstractGatewayFilterFactory<RateLimitFilter.Config> {
private final RedisScript<Long> script = RedisScript.of(
"local bucket = KEYS[1] " +
"local rate = tonumber(ARGV[1]) " +
"local capacity = tonumber(ARGV[2]) " +
"local now = tonumber(ARGV[3]) " +
"local requested = tonumber(ARGV[4]) " +
"local fill_time = capacity/rate " +
"local ttl = math.floor(fill_time*2) " +
"local last = redis.call('HMGET', bucket, 'last', 'tokens') " +
"local last_tokens = tonumber(last[2]) or capacity " +
"local last_refreshed = tonumber(last[1]) or now " +
"local delta = math.max(0, now-last_refreshed) " +
"local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) " +
"if filled_tokens < requested then return -1 end " +
"redis.call('HMSET', bucket, 'last', now, 'tokens', filled_tokens-requested) " +
"redis.call('EXPIRE', bucket, ttl) " +
"return filled_tokens-requested", Long.class);
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String key = "rate:" + exchange.getRequest().getURI().getPath();
Long remain = stringRedisTemplate.execute(script,
Collections.singletonList(key),
String.valueOf(config.rate), String.valueOf(config.capacity),
String.valueOf(Instant.now().toEpochMilli()), "1");
if (remain < 0) {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
};
}
public static class Config {
public int rate = 3000; // 每秒生成的令牌数
public int capacity = 6000; // 桶容量
}
}
在application.yml中针对霸王餐下单接口单独限流:
spring:
cloud:
gateway:
routes:
- id:霸王餐-order
uri: lb://baidican-order
predicates:
- Path=/api/v1/baidican/order/**
filters:
- name: RateLimit
args:
rate: 3000
capacity: 6000
三、熔断:Resilience4j快速失败
目标:错误率超5%或响应时长>P99(800ms)即熔断3s,直接返回兜底文案“活动太火爆,稍后再试”。实现:
package cn.juwatech.gateway.circuit;
@Configuration
public class CircuitConfig {
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {
return factory -> factory.configureDefault(id ->
Resilience4JConfigBuilder.of(id)
.circuitBreakerConfig(CircuitBreakerConfig.custom()
.failureRateThreshold(5)
.waitDurationInOpenState(Duration.ofSeconds(3))
.slidingWindowType(SlidingWindowType.COUNT_BASED)
.slidingWindowSize(100)
.build())
.timeLimiterConfig(TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofMillis(800))
.build())
.build());
}
}
在路由级别开启熔断:
filters:
- name: CircuitBreaker
args:
name: baidican-cb
fallbackUri: forward:/fallback/baidican
兜底接口:
@RestController
@RequestMapping("/fallback")
public class FallbackController {
@GetMapping("/baidican")
public Map<String,String> baidicanFallback(){
return Collections.singletonMap("msg","活动太火爆,稍后再试");
}
}
四、灰度:Ribbon元数据驱动
需求:新版本v2仅对内部员工手机号开放,其他用户仍访问v1。步骤:
- 在Nacos给实例打标签:
{"version":"v2","gray":"true"}
- 自定义
GrayLoadBalancer:
package cn.juwatech.gateway.loadbalancer;
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final ObjectProvider<ServiceInstanceListSupplier> supplier;
private final String serviceId;
public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> supplier, String serviceId) {
this.supplier = supplier;
this.serviceId = serviceId;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
return supplier.getIfAvailable().get().next()
.map(instances -> {
HttpHeaders headers = ((RequestData) request.getContext()).getHeaders();
String mobile = headers.getFirst("X-Mobile");
boolean isEmployee = EmployeeUtils.isEmployee(mobile);
List<ServiceInstance> filtered = instances.stream()
.filter(i -> isEmployee ? "v2".equals(i.getMetadata().get("version"))
: "v1".equals(i.getMetadata().get("version")))
.collect(Collectors.toList());
return new DefaultResponse(filtered.get(ThreadLocalRandom.current().nextInt(filtered.size())));
});
}
}
- 替换默认Rule:
@Bean
public GrayLoadBalancer grayLoadBalancer(Environment env,
LoadBalancerClientFactory clientFactory) {
String name = env.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new GrayLoadBalancer(clientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
五、全链路压测结果
4核8G容器,单网关节点:
- 限流:QPS 3000时拒绝率0.3%,RT 99ms
- 熔断:后端错误率飙至10%时,3s内触发,成功率恢复至99%
- 灰度:员工号码100%命中v2,普通用户0%误闯
六、动态配置一键下发
利用Nacos @RefreshScope,灰度比例、限流阈值、熔断参数均支持热更新,无需重启网关。
示例:将限流阈值从3000→5000,改配置后5s内全部节点生效。
七、上线 checklist
- Redis集群与网关同机房,RT<1ms
- 熔断兜底接口返回JSON,统一Content-Type
- 灰度标签只对内部员工手机号生效,防止游客串流
- 监控:Prometheus采集
resilience4j_circuitbreaker_calls与自定义rate_limit_reject_total
本文著作权归吃喝不愁app开发者团队,转载请注明出处!
2671

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



