SpringCloud网关如何做限流?

1、SpringGateWay

在Spring Cloud Gateway中实现限流的一个常见方法是使用Redis RateLimiter。Spring Cloud Gateway提供了内置的RateLimiter API,它可以配合Redis使用,实现对请求的限流。以下是使用Redis RateLimiter进行限流的基本步骤:

1. 引入依赖

首先,确保你的项目中引入了Spring Cloud Gateway和Spring Boot Redis的依赖。

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

确保已经安装配置好Redis服务,Spring Cloud Gateway将利用这个服务进行限流数据的存储与查询。

2. 配置Redis信息


在application.yml或application.properties中配置你的Redis信息:

spring:
  redis:
    host: localhost
    port: 6379

3. 配置限流规则

application.yml中配置Spring Cloud Gateway的路由规则,并定义Redis RateLimiter作为限流策略。以下示例展示了如何配置一个简单的限流规则:

spring:
  cloud:
    gateway:
      routes:
        - id: my_route_id
          uri: http://example.com
          predicates:
            - Path=/api/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                key-resolver: "#{@userKeyResolver}"

在上述配置中,replenishRateburstCapacity分别控制了令牌桶的填充速率和容量,key-resolver用于定义如何解析限流的键(比如根据客户端IP地址或用户标识进行限流)。

4. 定义Key Resolver

key-resolver是一个指向Spring Bean的引用,这个Bean需要实现KeyResolver接口,用于决定限流规则的作用对象(如用户、IP等)。以下是一个按请求路径进行限流的KeyResolver示例:

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Configuration
public class RateLimiterConfiguration {

    @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getPath().toString());
    }
}

5. 启动并测试

启动你的Spring Boot应用并测试限流。当请求的频率超过了replenishRate定义的阈值时,客户端将会收到429 Too Many Requests的响应。

以上就是在Spring Cloud Gateway中使用Redis RateLimiter进行限流的基本步骤。根据你的实际需求,可能还需要对限流策略进行更细致的调整,包括混合使用不同的KeyResolver来实现复杂的限流逻辑等。

2、ZUUL

需要借助其他组件或服务来达到限流目的,比如Netflix的Hystrix、阿里巴巴的Sentinel或自定义过滤器结合第三方缓存服务(例如Redis)。

以下是使用Netflix Hystrix和自定义Zuul过滤器实现限流的一种方式:

1. 添加Hystrix依赖:

如果你的项目中尚未包含Hystrix,请首先添加以下依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2. 开启Hystrix和Hystrix Dashboard(可选):

确保你的应用中启用了Hystrix和Hystrix的监控面板(Dashboard):

@EnableZuulProxy
@EnableCircuitBreaker
@EnableHystrixDashboard // 如果你想开启监控面板
@SpringBootApplication
public class YourApplication {
    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}

3. 创建一个自定义的Zuul过滤器:

创建一个Zuul过滤器来实现限流逻辑,你可以使用Hystrix来控制并发量。

public class RateLimitZuulFilter extends ZuulFilter {

    private static final RateLimiter RATE_LIMITER = RateLimiter.create(100); // 每秒100个请求的速率

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return -4; // 设置为高优先级
    }

    @Override
    public boolean shouldFilter() {
        return true; // 永远开启过滤
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletResponse response = ctx.getResponse();

        if (!RATE_LIMITER.tryAcquire()) {
            ctx.setSendZuulResponse(false); // 限流发生时,拒绝访问
            ctx.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value()); // 返回429状态码
            ctx.setResponseBody("Too many requests"); // 设置返回的内容
            response.setContentType("application/json;charset=UTF-8");
        }
        return null;
    }
}

在上面的示例中,我们使用了com.google.common.util.concurrent.RateLimiter类(来自Guava库)作为一个简单的令牌桶实现方式。

4. 注册Zuul过滤器:

在Spring Application中注册上面创建的Zuul过滤器。

@Bean
public RateLimitZuulFilter rateLimitZuulFilter() {
    return new RateLimitZuulFilter();
}

完成以上步骤后,每当请求到达Zuul时,都会经过这个限流过滤器,如果超出了预定的限流标准,就会返回HTTP 429 - Too Many Requests响应。

对于更复杂的限流策略,比如基于用户或者IP进行限流,你需要在自定义过滤器中实现额外的逻辑,或者考虑使用如Sentinel这样的第三方限流组件。Sentinel可以与Spring Cloud Alibaba整合,提供更丰富的限流、降级和熔断策略。

3、使用Alibaba nacos sentinel

完全注解式+配置就可以搞定,他的底层原理也是些限流算法,见4。

4、手动写

纯手写限流器可以从多种算法思路出发,主要包括固定窗口计数器、滑动窗口日志、令牌桶和漏桶算法。每种算法都有其特定的应用场景和优缺点。以下分别介绍这些思路:

1. 固定窗口计数器(Fixed Window Counter)

原理:将时间分割成等长的窗口,每个窗口内维护一个计数器,记录该窗口内的请求次数。当请求到达时,增加计数器的值,如果计数器的值超过预设的阈值,则拒绝后续的请求直到下一个窗口开始。

优点:实现简单,性能较好。 缺点:时间边界上可能会出现请求的瞬时双倍放行,因为窗口切换时,计数器重置,导致限流不够平滑。

2. 滑动窗口日志(Sliding Window Log)

原理:记录每个请求的时间戳,在每次请求到来时,清理掉时间窗口外的请求记录,并检查当前窗口内的请求总数是否超限。

优点:限流更加平滑,避免了固定窗口算法的边界问题。 缺点:随着请求量的增加,内存和计算开销较大,因为需要存储窗口内所有请求的时间戳。

3. 令牌桶(Token Bucket)

原理:一个初始为空的桶,以固定的速率往桶里添加令牌,每个到来的请求都需要获取一个令牌。如果能够获取到令牌,则请求被处理;如果令牌不足,则请求被拒绝。

优点:可以应对突发流量,提供一定程度的弹性和平滑限流。 缺点:实现相对复杂,需要维护令牌的生成和消耗。

4. 漏桶(Leaky Bucket)

原理:请求先进入到一个桶里,然后以限定的速度从桶中流出处理。即使短时间内大量请求涌入,流出速度也是恒定的。

优点:输出流量非常平稳。 缺点:对于突发流量的响应不如令牌桶灵活。

令牌桶算法代码:

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

// 令牌桶限流器类
public class TokenBucketLimiter {
    // 当前令牌数量
    private final AtomicInteger tokens;
    // 令牌桶的容量
    private final int capacity;
    // 令牌的生成速率(每秒生成令牌数)
    private final int tokenRate;
    // 定时任务执行器,用于生成令牌
    private final ScheduledExecutorService scheduler;

    // 构造函数初始化令牌桶限流器
    public TokenBucketLimiter(int capacity, int tokenRate) {
        this.capacity = capacity; // 设置令牌桶容量
        this.tokenRate = tokenRate; // 设置令牌生成速率
        this.tokens = new AtomicInteger(0); // 初始化令牌数量为0
        this.scheduler = Executors.newSingleThreadScheduledExecutor();
        startTokenRefill(); // 开始补充令牌
    }

    // 开启定时补充令牌到桶中
    private void startTokenRefill() {
        // 定时以固定的速率放入令牌
        scheduler.scheduleAtFixedRate(() -> {
            if (tokens.get() < capacity) {
                // 如果令牌数未达到上限,则添加令牌
                tokens.incrementAndGet();
            }
        }, 0, 1000 / tokenRate, TimeUnit.MILLISECONDS); // 时间单位为毫秒
    }

    // 尝试从令牌桶中获取令牌
    public boolean tryAcquire() {
        // 令牌数大于0时,减少一个令牌,并返回true表示获取成功
        while (true) {
            int existingTokens = tokens.get();
            if (existingTokens <= 0) {
                return false; // 获取失败,没有足够的令牌
            }
            if (tokens.compareAndSet(existingTokens, existingTokens - 1)) {
                return true; // 成功减少一个令牌
            }
            // CAS 更新失败,循环重试
        }
    }

    // 应用程序结束时应该调用的清理方法,关闭定时任务执行器避免内存泄漏
    public void stop() {
        scheduler.shutdown();
    }

    // 演示如何使用令牌桶限流器
    public static void main(String[] args) throws InterruptedException {
        // 创建一个容量为10,每秒生成5个令牌的令牌桶
        TokenBucketLimiter limiter = new TokenBucketLimiter(10, 5);

        // 模拟请求
        for (int i = 0; i < 20; i++) {
            // 尝试获取令牌
            if (limiter.tryAcquire()) {
                System.out.println("Request allowed at " + System.currentTimeMillis());
            } else {
                System.out.println("Request denied at " + System.currentTimeMillis());
            }
            // 模拟请求的间隔时间
            Thread.sleep(100);
        }

        //

在这个限流器中,我们使用了AtomicInteger来确保线程安全地对令牌数量进行操作,并用compareAndSet方法来预防并发时的问题。此外,我们添加了一个关闭方法shutdown()用以关闭我们创建的定时器任务。

记得在实际生产环境中优雅地关闭ScheduledExecutorService,以防止资源泄露。如果您打算在生产环境中部署这样的限流器,可能还需要增加更多的特性,如动态调整限流策略、监控与告警、记录日志等。

漏桶算法代码:

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

// 漏桶限流器类
public class LeakyBucketLimiter {
    // 当前桶中的水量,也就是目前的请求量
    private final AtomicInteger water;
    // 桶的容量限制,当水量(请求量)超过容量时将不再接受新的请求
    private final int capacity;
    // 水的漏出速率(请求处理速率),以每秒固定数量的请求处理速度
    private final int leakRate;
    // 定时任务执行器,用于以一定的速率漏水(处理请求)
    private final ScheduledExecutorService scheduler;

    public LeakyBucketLimiter(int capacity, int leakRate) {
        this.capacity = capacity; // 设置桶的容量
        this.leakRate = leakRate; // 设置漏水速率,即每秒可以处理的请求数
        this.water = new AtomicInteger(0); // 初始化水量(请求量)为0
        this.scheduler = Executors.newSingleThreadScheduledExecutor();
        startLeakage(); // 开始模拟漏水(请求处理)
    }

    // 开始水的漏出过程(开始处理请求的过程)
    private void startLeakage() {
        scheduler.scheduleAtFixedRate(() -> {
            if (water.get() > 0) {
                // 桶内有水的情况下,每间隔固定时间就漏一滴水(处理一个请求)
                water.decrementAndGet();
            }
        }, 0, 1000 / leakRate, TimeUnit.MILLISECONDS); // 以固定频率减少水量
    }

    // 尝试将水(请求)放入桶中
    public boolean tryAcquire() {
        // 判断当前水量是否小于桶的容量
        if (water.get() < capacity) {
            // 若桶未满,则加入一滴水(接受一个请求)
            // 并检查加水后是否溢出,若不溢出则表示请求被接受
            return water.incrementAndGet() <= capacity;
        }
        // 若桶已满,则拒绝新的水滴(拒绝新的请求)
        return false;
    }

    // 应用程序结束时应该调用的清理方法,关闭定时任务执行器避免内存泄漏
    public void stop() {
        scheduler.shutdown();
    }

    // 测试漏桶限流器
    public static void main(String[] args) throws InterruptedException {
        // 创建一个桶容量为10,漏水速率为每秒5的漏桶
        LeakyBucketLimiter limiter = new LeakyBucketLimiter(10, 5);

        // 模拟请求
        for (int i = 0; i < 20; i++) {
            // 尝试获取请求
            if (limiter.tryAcquire()) {
                System.out.println("Request allowed at " + System.currentTimeMillis());
            } else {
                System.out.println("Request denied at " + System.currentTimeMillis());
            }
            // 模拟请求间隔时间
            Thread.sleep(100);
        }

        // 中止漏桶限流器
        limiter.stop();
    }
}

代码中的LeakyBucketLimiter类实现了一个简单的漏桶算法的限流器。类的属性包括当前的水量(代表当前请求数量),桶的最大容量,以及漏水(实际处理请求)的速率。通过定时任务定期减少水量,模拟请求的处理。

当尝试添加一个新的请求时(tryAcquire 方法被调用),它首先检查桶的水量是否已满。如果没有,它允许新的水加入桶中(允许新请求进入),否则拒绝(拒绝新请求)。

A:Spring Cloud Gateway是一个基于Spring Framework5,Spring Boot 2和Project Reactor的网关,它提供了一组强大的API来构建基于路由的API网关Spring Cloud Gateway中的限流可以通过使用Spring Cloud限流组件来实现,可以使用Spring Cloud Alibaba Sentinel对Spring Cloud Gateway进行限流。 在Spring Cloud Gateway实现限流的一种方法是使用网关过滤器。可以创建一个自定义的网关过滤器,在该过滤器中实现限流逻辑,并将其与路由规则关联。在实现该过程时,需要了解以下三个关键组件。 1. 限流器(Rate Limiter) 在Spring Cloud Gateway实现限流,需要使用一个限流器,以控制API请求的速率。限流器会统计API请求的速率,并在达到预先设定的阈值时拒绝部分或全部请求。可以使用Spring Cloud限流组件中提供的限流器来实现。 2. GatewayFilter GatewayFilter是一个Spring Cloud Gateway的基本组件,用于处理传入的HTTP请求GatewayFilter可以通过过滤器链来连接,可以在过滤器链中的任何一个过滤器中实现限流逻辑。在使用Spring Cloud限流组件时,可以创建自定义的GatewayFilter,以在其中实现限流逻辑。 3. 路由规则(Route) 在Spring Cloud Gateway中,路由规则指的是将传入的请求映射到相应的服务或URL的规则。可以通过将限流组件与路由规则关联,以实现对特定API请求的流量控制。 总体来说,可以通过自定义网关过滤器,并将其与路由规则关联,以实现Spring Cloud Gateway限流。需要使用Spring Cloud限流组件中提供的限流器来实现限流功能。同时,需要注意限流对性能带来的影响,以及如何维护阈值和监视限流效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

济南大飞哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值