You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Spring Cloud Gateway实现按用户订阅区分的API限流

基于用户订阅等级的Spring Cloud Gateway动态限流实现

要实现同一API根据用户订阅等级使用不同限流规则,核心是动态选择对应的RedisRateLimiter实例,而非固定绑定某一个。以下是具体实现步骤:

1. 定义订阅等级标识

先统一用户订阅等级的枚举,方便后续逻辑判断:

public enum SubscriptionLevel {
    CONSUMER_50TPS,
    CONSUMER_100TPS
}

2. 实现用户等级获取服务

编写服务类,根据用户名获取对应的订阅等级(实际可从数据库、Redis缓存或JWT Claims中读取):

@Service
public class UserSubscriptionService {

    // 示例逻辑:根据用户名返回订阅等级,实际替换为业务逻辑
    public SubscriptionLevel getSubscriptionLevel(String username) {
        // 比如VIP用户前缀匹配返回100TPS等级,普通用户返回50TPS
        return username.startsWith("vip_") 
                ? SubscriptionLevel.CONSUMER_100TPS 
                : SubscriptionLevel.CONSUMER_50TPS;
    }
}

3. 自定义动态限流处理器

创建一个动态RateLimiter,根据用户等级自动切换对应的RedisRateLimiter实例:

@Component
public class DynamicRedisRateLimiter implements RateLimiter<String> {

    private final RedisRateLimiter consumer50tps;
    private final RedisRateLimiter consumer100tps;
    private final UserSubscriptionService subscriptionService;

    // 通过构造注入获取已定义的两个限流实例和等级服务
    public DynamicRedisRateLimiter(RedisRateLimiter consumer50tps,
                                  RedisRateLimiter consumer100tps,
                                  UserSubscriptionService subscriptionService) {
        this.consumer50tps = consumer50tps;
        this.consumer100tps = consumer100tps;
        this.subscriptionService = subscriptionService;
    }

    @Override
    public Mono<Response> isAllowed(String routeId, String username) {
        // 根据用户名获取等级,选择对应的限流规则
        SubscriptionLevel level = subscriptionService.getSubscriptionLevel(username);
        RateLimiter<String> targetLimiter = switch (level) {
            case CONSUMER_100TPS -> consumer100tps;
            default -> consumer50tps;
        };
        // 调用对应限流实例的判断逻辑
        return targetLimiter.isAllowed(routeId, username);
    }
}

4. 修改网关路由配置

将原来固定的consumer50tps替换为自定义的动态限流处理器:

@Bean
public RouteLocator appRouteConfig(RouteLocatorBuilder builder, 
                                  DynamicRedisRateLimiter dynamicRedisRateLimiter,
                                  KeyResolver userKeyResolver) {
    return builder.routes()
            .route(p -> p.path("/microservice-one/api1/**")
                    .filters(f -> f
                            .rewritePath("/microservice-one/(?<segment>.*)", "/${segment}")
                            .requestRateLimiter(r -> r.setRateLimiter(dynamicRedisRateLimiter)
                                    .setKeyResolver(userKeyResolver) // 保留原有用户名提取逻辑
                            ))
                    .uri("lb://microservice-one"))
            .build();
}

5. 保留原有基础配置

原有的RedisRateLimiter实例和用户KeyResolver继续保留:

@Bean
public RedisRateLimiter consumer50tps() {
    return new RedisRateLimiter(50, 50, 1);
}

@Bean
public RedisRateLimiter consumer100tps() {
    return new RedisRateLimiter(100, 100, 1);
}

@Bean
public KeyResolver userKeyResolver() {
    return exchange -> {
        // 保留原有从JWT提取用户名的逻辑,示例如下
        String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String jwtToken = authHeader.substring(7);
            // 使用JWT解析库(如JJWT)解析获取用户名
            Claims claims = Jwts.parser()
                    .setSigningKey("your_jwt_secret_key")
                    .parseClaimsJws(jwtToken)
                    .getBody();
            return Mono.just(claims.getSubject());
        }
        return Mono.empty();
    };
}

关键注意事项

  • 性能优化:用户订阅等级建议缓存到Redis,避免每次请求都查询数据库,降低延迟
  • JWT扩展:如果JWT中已包含订阅等级字段,可直接从Claims中提取,省去服务调用
  • 扩展性:后续新增等级只需在SubscriptionLevel枚举中添加,并在动态限流处理器中补充对应分支即可

内容的提问来源于stack exchange,提问作者Imranmadbar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:22:45