基于Spring Cloud Gateway的外卖霸王餐API限流熔断与灰度发布实践

基于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。步骤

  1. 在Nacos给实例打标签:
{"version":"v2","gray":"true"}
  1. 自定义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())));
            });
    }
}
  1. 替换默认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

  1. Redis集群与网关同机房,RT<1ms
  2. 熔断兜底接口返回JSON,统一Content-Type
  3. 灰度标签只对内部员工手机号生效,防止游客串流
  4. 监控:Prometheus采集resilience4j_circuitbreaker_calls与自定义rate_limit_reject_total

本文著作权归吃喝不愁app开发者团队,转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值