spring-gateway 自定义限流

某个服务需要对它的所有接口进行流量控制,已降低服务器资源的利用

pom: (jdk1.8)

java 复制代码
<spring-boot.version>2.7.13</spring-boot.version>
        <spring-cloud.version>2021.0.8</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>

 
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

filter

java 复制代码
import com.sikaryofficial.common.core.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.text.MessageFormat;
import java.time.Duration;

/**
 * @author : qinjinyuan
 * @desc : TODO  请填写你的功能描述
 * @date : 2024/08/14 15:26
 */
@Component
@Slf4j
public class RateLimitingFilterFactory extends AbstractGatewayFilterFactory<Object> {
    private static final int MAX_REQUESTS_PER_SECOND = 2;
    /**
     * 3秒内请求2次
     */
    private static final Duration EXPIRATION_TIME = Duration.ofSeconds(3);
    private static final String GATEWAY_RATE_LIMIT_KEY = "gateway:rate:limit:{0}";

    private final ReactiveRedisTemplate<Object, Object> reactiveRedisTemplate;

    public RateLimitingFilterFactory(ReactiveRedisTemplate<Object, Object> reactiveRedisTemplate) {
        super(Object.class);
        this.reactiveRedisTemplate = reactiveRedisTemplate;
    }

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath(); // 获取请求路径
            // 生成键
            String cacheKey = MessageFormat.format(GATEWAY_RATE_LIMIT_KEY, path);
            // 获取当前计数
            return reactiveRedisTemplate.opsForValue().increment(cacheKey, 1L)
                    .flatMap(count -> {
                        // 如果计数超过最大值,则拒绝请求
                        if (count > MAX_REQUESTS_PER_SECOND) {
                            return handleTooManyRequests(exchange);
                        } else {
                            // 设置过期时间
                            return reactiveRedisTemplate.expire(cacheKey, EXPIRATION_TIME)
                                    .then(chain.filter(exchange));
                        }
                    });
        };
    }

    private Mono<Void> handleTooManyRequests(ServerWebExchange exchange) {
        // 返回429 Too Many Requests HTTP状态码
        log.error("[Too Many Requests]请求路径:{}", exchange.getRequest().getPath());
          exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
        exchange.getResponse().getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        R<?> result = R.fail(HttpStatus.TOO_MANY_REQUESTS, "Too Many Requests");
        DataBuffer dataBuffer = exchange.getResponse().bufferFactory().wrap(JSON.toJSONString(result).getBytes());
        return exchange.getResponse().writeWith(Mono.just(dataBuffer));

    }


}

其中响应式 redis 配置如下

java 复制代码
@Configuration
@EnableCaching
@AutoConfigureBefore({RedisReactiveAutoConfiguration.class})
public class RedisConfig extends CachingConfigurerSupport
{
    

    /**
     * {@code @ConditionalOnMissingBean(name = "reactiveRedisTemplate")}
     * 表示组件可覆盖
     *
     * @see org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration
     * #reactiveRedisTemplate(ReactiveRedisConnectionFactory, ResourceLoader)
     */
    @Bean
    @ConditionalOnMissingBean(name = "reactiveRedisTemplate")
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public ReactiveRedisTemplate<Object, Object> reactiveRedisTemplate(
            ReactiveRedisConnectionFactory reactiveRedisConnectionFactory) {
        /**
         *   // RedisSerializer.string()
         *         // StringRedisSerializer.UTF_8
         *         // RedisSerializer.json()
         *         // GenericJackson2JsonRedisSerialize
         */
        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
        RedisSerializationContext<Object, Object> serializationContext = RedisSerializationContext
                .newSerializationContext(RedisSerializer.string())
                .value(serializer)
                .build();
        return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, serializationContext);
    }

    
}

gateway 路由配置

python 复制代码
spring:
  redis:
    database: 1 
    host: localhost
    port: 6379
    password: 123456
    timeout: 6000ms  # 连接超时时长(毫秒)  
    lettuce:    
      pool:      
        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)      
        max-wait: -1ms      # 连接池最大阻塞等待时间(使用负值表示没有限制)      
        max-idle: 10      # 连接池中的最大空闲连接      
        min-idle: 5       # 连接池中的最小空闲连接
  cloud:
    gateway:
      discovery:
        locator:
          lowerCaseServiceId: true
          enabled: true
      routes:       
        # xxx 知识问答服务
        - id: xxx-qa-conversation
          uri: http://192.168.5.180:5000
          predicates:
            - Path=/qa/conversation/**
          filters:
            - StripPrefix=2
            # 服务限流控制过滤器
            - RateLimitingFilterFactory

这样就完成了一个完整的限流功能。

代码解释:

限流逻辑:

代码中的限流逻辑是基于每个路径的总请求数进行的,而不是在特定时间窗口内的请求数。这意味着如果一个路径在短时间内收到大量请求,只要总数不超过 MAX_REQUESTS_PER_SECOND,请求就会被允许通过

所以,如果需要根据当前用户来限流,在缓存key中,添加userID 来判定即可

相关推荐
爬山算法18 分钟前
Maven(28)如何使用Maven进行依赖解析?
java·maven
2401_8574396942 分钟前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧66644 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
李老头探索1 小时前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
芒果披萨1 小时前
Filter和Listener
java·filter
qq_4924484461 小时前
Java实现App自动化(Appium Demo)
java
阿华的代码王国1 小时前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
找了一圈尾巴2 小时前
前后端交互通用排序策略
java·交互
哎呦没4 小时前
SpringBoot框架下的资产管理自动化
java·spring boot·后端
m0_571957586 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解