四. Gateway 限流

一. 限流中的基础问题

1. 为什么限流及常见限流方案

  1. 例如用户增长过快,当前服务配置情况下压力过大,热点事件,恶意请求等等,简单解释一下不做限流的危害
  2. 常见限流方案:
  1. Hystrix 线程池隔离,或信号量方式超过线程池的负载,走熔断的逻辑。在一般应用服务器中,比如tomcat容器也是通过限制它的线程数来控制并发的;
  2. 通过时间窗口的平均速度来控制流量。常见的限流纬度有比如通过Ip来限流、通过uri来限流、通过用户访问频次来限流。
  1. 一般限流都是在网关这一层做,比如Nginx、Openresty、kong、zuul、Spring Cloud Gateway等;也可以在应用层通过Aop这种方式去做限流。
  2. 通常情况下增加限流后会设置请求进行熔断降级处理
  1. 什么是熔断: 类似于保险丝,当某服务出现不可用或响应超时的情况,为了防止整个系统出现雪崩,暂停对服务的调用,上层设置请求直接返回
  2. 什么是降级: 从整个服务负荷情况出发和考虑,对某些负荷较高的情况,提供预备方案,当超过负载时执行预备方案或者直接走兜底的方法,从而释放服务器资源以保证核心业务,
  3. 实际都是为了保证整个系统可以正常对外提供服务,不同点是服务降级一般是从整体的负荷考虑,主动降级
  1. 通过熔断降级再次解释限流的必要性:

假设创建订单成功调用短信服务发送短信,假设创建订单服务在调用短信服务时,短信服务异常,可能会造成创建订单一直拿不到响应,请求堆积,最终造成后续订单也无法创建的问题(实际生产上这种发短信的业务可能通过mq异步执行,此处只是举个例子)

2. 常见限流算法

常用限流算法: 计数器(滑动窗口算法计数器),令牌桶算法(RateLimiter),漏桶算法(短时间内不能承受过大的流量),(代码层面实现限流)

计数器限流算法

  1. 计数器限流算法有两种: 传统计数器,与滑动窗口计数器
  2. 传统计数器方式存在的临界问题:

假设: 一秒钟内某个后端运行请求不超过10次,使用原子类针对于某个服务设置一个计数器,每请求一次计数器+1,当请求超过10此时,并且与第一次请求的时间间隔不超过一秒钟,说明请求过多,服务熔断,执行服务降级,给用户提示信息,如果与第一次请求间隔超过一秒钟,说明还在范围内重置计数器(思考问题: 传统的计数器方式,假设在第59到第61临界时突然发生了大量的请求,由于都在判断的阀值以内,可能会达不到限流的效果,使用滑动窗口算法实现计数器解决)

  1. 滑动窗口算法计数器, 可以解决传统计数器临界问题:

假设每分钟允许向后台请求60次,可以将这60次请求分为6份,每秒钟允许请求10此,通过滑动进行判断,解决临界问题,(是不是可以看为,当到达一分钟时滑动一个,在滑动式也会计算每一份的效率,将60次请求分为6个窗口,每个窗口为10秒10次,当到达1分钟时滑动包含超过1分钟的另外一份,如果在一份中内判断超过60次请求进行熔断,降级,如果在滑动包含时每个窗口的效率每10秒超过10次也熔断,降级,滑动以后,如果滑动以后没有到达熔断的条件第一个窗口初始化为开始值)
在这里插入图片描述

令牌桶算法

  1. 令牌桶有两个动作:
  1. 固定速率,向桶中添加令牌
  2. 客户端如果发送请求向后台,首先要获取桶中的令牌
  1. 令牌桶中存放有固定容量的令牌,会开启独立线程按照固定的速率向桶中添加令牌,假设每秒向桶中添加2个令牌,客户端请求服务端时需要先在令牌桶中获取令牌,例如每秒有10获取令牌的请求,但是每秒只生成2个令牌,其它8获取不到令牌的请求就会被服务降级,假设每秒只发送一个请求,此时消费令牌的速度过慢,就需要通过令牌桶的容量进行限制,当令牌桶存满时,则不会再去添加令牌(令牌桶优点可以接受突然的高并发)

漏桶算法

  1. 漏桶算法内部有一个容器,当请求进来时,相当入将请求通过容器匀速的流出,这样不管上层接收多少请求,下面流出的速度始终保持不变,因为处理的速度是固定的,请求进来的速度是未知的,没来得及处理的请求就先放在桶里,桶的容量是有限的,如果满了,新进来的请求走降级策略
  2. 漏桶算法存在一个缺陷,无法应对短时间的突发流量

3. 几种基础版限流实现方案

基于redis实现限流

  1. 基础版, 定时生成token存储redis,并对token个数进行限制,请求执行前先通过redis获取令牌,获取不到则执行降级策略
  2. 进阶:redis实现滑动窗口限流, 使用 zset 记录 IP 访问次数,每个 IP 通过 key 保存下来,score 保存当前时间戳,value 唯一用时间戳或者UUID来实现,当接收请求时,时间分数作为窗口限制,获取该窗口记录行数,判断是否超过限制,若超过则执行降级
public class RedisLimiterTest {
    private Jedis jedis;

    public RedisLimiterTest(Jedis jedis) {
        this.jedis = jedis;
    }

    /**
     * @param ipAddress Ip地址
     * @param period    特定的时间内,单位秒
     * @param maxCount  最大允许的次数
     * @return
     */
    public boolean isIpLimit(String ipAddress, int period, int maxCount) {
        String key = String.format("ip:%s", ipAddress);
        // 毫秒时间戳
        long currentTimeMillis = System.currentTimeMillis();
        Pipeline pipe = jedis.pipelined();
        // redis事务,保证原子性
        pipe.multi();
        // 存放数据,value 和 score 都使用毫秒时间戳
        pipe.zadd(key, currentTimeMillis, "" + UUID.randomUUID());
        // 移除窗口区间所有的元素
        pipe.zremrangeByScore(key, 0, currentTimeMillis - period * 1000);
        // 获取时间窗口内的行为数量
        Response<Long> count = pipe.zcard(key);
        // 设置 zset 过期时间,避免冷用户持续占用内存,这里宽限1s
        pipe.expire(key, period + 1);
        // 提交事务
        pipe.exec();
        pipe.close();
        // 比较数量是否超标
        return count.get() > maxCount;
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        RedisLimiterTest limiter = new RedisLimiterTest(jedis);
        for (int i = 1; i <= 20; i++) {
            // 验证IP  10秒钟之内只能访问5次
            boolean isLimit = limiter.isIpLimit("222.73.55.22", 10, 5);
            System.out.println("访问第" + i + "次, 结果:" + (isLimit ? "限制访问" : "允许访问"));
        }
    }
}

基于 Guava RateLimiter 实现令牌算法

  1. 项目中引入依赖
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.2-jre</version>
</dependency>
  1. 实现流程
  1. 创建操作限流对象,并设置向令牌桶中添加令牌的速率
  2. 指定等待时间内获取锁,
  3. 如果指定时间内获取不到锁,服务降级给用户提示信息
  4. 如果指定时间内获取到锁调用生成订单的逻辑方法
  5. 此处有一个问题(没有设置令牌桶存满的个数)
  1. 重点逻辑
  1. 使用RateLimiter 实现令牌桶算法, 创建RateLimiter对象,设置限流规则
  2. 通过RateLimiter 调用tryAcquire()尝试获取令牌,并设置等待时间
  3. tryAcquire()空参方法获取令牌,如果令牌获取不到则线程会一直等待,tryAcquire(指定等待时间,单位)方法设置指定等待时间,指定时间内返回是否获取到令牌的布尔值,如果获取不到令牌线程也不会一直等待
  1. 代码示例
import com.google.common.util.concurrent.RateLimiter;
import com.shsxt.orderService.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class IndexController {
    @Autowired
    private OrderService orderService;
    // 解释:1.0 表示 每秒中生成1个令牌存放在桶中(以秒为单位的固定速率值)
    // 如果方法中传入的为"10.0"则表示每秒中向桶中存放10个令牌
    RateLimiter rateLimiter = RateLimiter.create(1.0);//此方法是一个独立线程所开启的,不受其他线程影响

    // 下单请求
    @RequestMapping("/order")
    public String order() {
        // 1.限流判断
        // 当前请求线程在令牌桶中拿到令牌的等待时间
        double b = rateLimiter.acquire();
        //2.判断请求在指定时间内是否获取到令牌,假设此时每秒会有10个客户端发起抢购,
        // 请求该接口,由于令牌桶每秒只能生成1个,其他9个获取不到令牌的请求会等待,
        // 通过tryAcquire方法设置获取令牌的等待时间为500毫秒,假设500毫秒内获取不到令牌
        // 则请求不会继续等待,返回false,走if语句内,相当于服务降级,返回给用户
        // 提示信息,提示用户当前系统繁忙请稍后再试
        boolean acquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);
        //rateLimiter.tryAcquire();//该方式获取令牌,如果获取不到令牌,线程会一直等待
        if (!acquire) {
            return "当前系统繁忙请稍后再试";//获取令牌失败服务降级,给出用户提示信息,
        }

        // 3.如果没有达到限流的要求或者500毫秒内获取到了令牌,直接调用订单接口
        //   获取订单是否生成成功,判断抢购是否成功,给用户提示信息
        boolean isOrderAdd = orderService.addOrder();
        if (isOrderAdd) {
            return "恭喜您,抢购成功!";
        }
        return "抢购失败!";//此处是虽然获取到了令牌,但是生成订单时由于某种原因失败而定义的
    }
}

二. Gateway Redis 令牌桶实现限流案例

  1. 在Spring Cloud Gateway官方提供了一个类RequestRateLimiterGatewayFilterFactory,适用Redis和lua脚本实现了令牌桶的方式。具体实现逻辑在RequestRateLimiterGatewayFilterFactory类中,lua脚本在如下图所示的文件夹中:
    在这里插入图片描述
  2. 实现步骤
  1. 创建项目引入依赖,包括Gateway,与redis
  2. 项目中增加限流标识,也就是进行限流判断时,是否限流的判断依据
  3. 配置限流速率
<!--基于Redis实现限流-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    <version>2.2.10.RELEASE</version>
</dependency>
  1. 增加限流标识,限流通常要根据某一个参数值作为依据来限流,比如每个IP每秒钟只能访问2次,此时是以IP为参考依据,我们创建根据IP限流的对象,该对象需要实现接口KeyResolver:
public class IpKeyResolver implements KeyResolver {

    /***
     * 根据IP限流
     * @param exchange
     * @return
     */
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
}
  1. 将限流标识对象注入到Spring容器
@Bean(name = "ipKeyResolver")
public KeyResolver userIpKeyResolver() {
    return new IpKeyResolver();
}
  1. yaml文件中配置限流速率等等
spring:
	cloud:
        gateway:
          routes:
            #商品服务
            - id: goods_route
              uri: lb://mall-goods
              predicates:
                - Path=/mall/brand/**
              filters:
                - StripPrefix=1
                # 指定过滤器
                - name: RequestRateLimiter
                  args:
                    # 指定限流标识
                    key-resolver: '#{@ipKeyResolver}' #对应上面注入到容器中的限流标识对象名,用于限流的键的解析器的Bean对象的名字.会使用 SpEL 表达式根据#{@beanName}从Spring容器中获取Bean对象
                    # 速率限流
                    redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
                    # 能容纳的并发流量总数
                    redis-rate-limiter.burstCapacity: 2 #令牌桶总容量

三. Gateway整合Sentinel实现网关限流

  1. Sentinel 支持对 SpringCloudGateway、Zuul 等主流的 API Gateway 进行限流, Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:
  1. GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
  2. ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /foo/** 和 /baz/** 的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流
  1. 网关流控实现原理
  1. 通过 GatewayRuleManager 加载网关流控规则GatewayFlowRule时,Sentinel 底层会将网关流控规则转化为热点参数规则(ParamFlowRule).存储在 GatewayRuleManager 中,与正常的热点参数规则相隔离。转换时 Sentinel 会根据请求属性配置,为网关流控规则设置参数索引(idx),并同步到生成的热点参数规则中
  2. 外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,会依次进行: 路由/API 分组匹配、请求属性解析和参数组装。

2.1 Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序组装参数数组,最终传入 SphU.entry(res, args) 中
2.2 Sentinel API Gateway Adapter Common 模块向 Slot Chain 中添加了一个 GatewayFlowSlot,专门用来做网关规则的检查。GatewayFlowSlot 会从 GatewayRuleManager 中提取生成的热点参数规则,根据传入的参数依次进行规则检查。若某条规则不针对请求属性,则会在参数最后一个位置置入预设的常量,达到普通流控的效果

基础使用示例

  1. 项目中已入依赖(注意不同版本Gateway与Sentinel的引入依赖可能不同)
<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
  1. 配置
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1     # nacos注册地址
      config:
        server-addr: 127.0.0.1     # 远程配置地址
    sentinel:
      transport:
        dashboard: localhost:8080       # sentinel注册地址
      # 直接建立心跳
      eager: true
      scg:
        # 限流后的响应配置
        fallback:
          content-type: application/json
          # 模式 response、redirect
          mode: response
          # 响应状态码
          response-status: 429
          # 响应信息
          response-body: 对不起,已经被限流了!!!
    gateway:
      # spring cloud gateway 路由配置方式
      discovery:
        locator:
          enabled: true                 #   表明gateway开启服务注册和发现的功能
          lower-case-service-id: true   #   将请求路径上的服务名配置为小写
          route-id-prefix: route-id-   # 配置routeId的前缀,最终为 route-id-注册服务名
  1. 参考博客

自定义 GatewayFilter, 自定义API分组维度实现限流, 以及异常处理

  1. 再次解释 基于Sentinel的Gateway限流是通过其提供的Filter来完成的,使用时可以自定义注入对应的SentinelGatewayFilter实例以及SentinelGatewayBlockExceptionHandler实例
package cn.jack.config;
 
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
 
import javax.annotation.PostConstruct;
import java.util.*;
 
@Configuration
public class GatewayConfiguration {
 
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;
 
    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }
 
    // 配置限流的异常处理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }
 
    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
 
    /**
     * 配置初始化的限流參數
     */
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<GatewayFlowRule>();
        rules.add(
                new GatewayFlowRule("product_route")    // 资源名称,对应路由id
                    .setCount(1)        // 限流阈值
                    .setIntervalSec(5)   // 统计时间窗口(熔断时间),单位:秒。默认是1秒
        );
 
        GatewayRuleManager.loadRules(rules);
    }
 
    /**
     * 自定义限流异常页面,限流返回的信息
     */
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap();
                map.put("code", 0);
                map.put("message", "接口被限流了");
 
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(map));
            }
        };
 
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}
  1. 自定义API分组维度实现限流, 是一种更细粒度的限流规则定义。它可以对匹配到的接口进行限流
package cn.jack.config;
 
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.*;
 
@Configuration
public class GatewayConfiguration2 {
 
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;
 
    public GatewayConfiguration2(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }
 
    // 配置限流的异常处理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }
 
    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
 
    /**
     * 配置初始化的限流參數
     */
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<GatewayFlowRule>();
        rules.add(
                new GatewayFlowRule("product_api_001")    // 资源名称,可以是sentinel的API定义的分组
                    .setCount(1)        // 限流阈值
                    .setIntervalSec(5)   // 统计时间窗口(即时间段内达到阈值实行限流),单位:秒。默认是1秒
        );
 
        rules.add(
                new GatewayFlowRule("product_api_002")    // 资源名称,可以是sentinel的API定义的分组
                        .setCount(1)        // 限流阈值
                        .setIntervalSec(5)   // 统计时间窗口(即时间段内达到阈值实行限流),单位:秒。默认是1秒
        );
 
        GatewayRuleManager.loadRules(rules);
    }
 
    // 自定义API分组
    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
 
        Set predicateItemSet1 = new HashSet();
        predicateItemSet1.add(new ApiPathPredicateItem()
                .setPattern("/product-serv/product/api001/**")
                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); // 前缀匹配
        ApiDefinition api1 = new ApiDefinition("product_api_001")
                .setPredicateItems(predicateItemSet1);
 
        Set predicateItemSet2 = new HashSet();
        predicateItemSet2.add(new ApiPathPredicateItem()
                .setPattern("/product-serv/product/api002/demo1")
                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));  // 精确匹配
        ApiDefinition api2 = new ApiDefinition("product_api_002")
                .setPredicateItems(predicateItemSet2);
 
        definitions.add(api1);
        definitions.add(api2);
 
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
 
    /**
     * 自定义限流异常页面,限流返回的信息
     */
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap();
                map.put("code", 0);
                map.put("message", "接口被限流了");
 
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(map));
            }
        };
 
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}
  1. 参考博客
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Cloud Gateway中,可以通过多种方式实现限流。一种常见的方式是使用Hystrix来进行线程池隔离,当超过线程池的负载时,可以采取熔断的逻辑来限制并发。另一种方式是通过时间窗口的平均速度来控制流量,可以通过IP、URI或用户访问频次等维度进行限流。一般限流操作是在网关层面进行的,比如使用Nginx、Openresty、Kong、Zuul或Spring Cloud Gateway等。此外,还可以通过AOP等方式在应用层实现限流。 在Spring Cloud Gateway中,可以使用RequestRateLimiter过滤器来实现限流功能。这个过滤器的核心代码是org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter#isAllowed方法,可以根据自定义的限流策略继承AbstractRateLimiter来实现。在配置过滤器中,可以指定名字为RequestRateLimiter来调用该限流器的实现类。 限流在高并发系统中非常重要,一方面可以防止大量的请求导致服务器过载,使服务不可用,另一方面可以防止网络攻击。常见的限流算法包括计数器算法等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Spring Cloud Gateway 限流](https://blog.csdn.net/qq_33349086/article/details/107444976)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Gateway网关限流](https://blog.csdn.net/m0_37543627/article/details/117066783)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值