IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Cloud Gateway实现限流 -> 正文阅读

[Java知识库]Spring Cloud Gateway实现限流

Spring Cloud Gateway实现限流

背景

zuul切换为spring cloud gateway时,需要重新实现限流逻辑。本文主要整理了spring cloud gateway中如何实现限流。

zuul中的限流

之前zuul的限流是通过guava提供的令牌桶算法实现的,通过一个全局的过滤器,对所有经过网关的请求,以IP地址作区分进行限流。

引入guava依赖:

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
</dependency>

具体代码案例:

/**
 * 自定义过滤器
 *
 * @author yuanzhihao
 * @since 2022/4/27
 */
@Component
@Slf4j
public class RequestRateLimitFilter implements Filter {

    private static final Cache<String, RateLimiter> RATE_LIMITER_CACHE = CacheBuilder
            .newBuilder()
            .maximumSize(1000)
            .expireAfterAccess(1, TimeUnit.HOURS)
            .build();

    private static final double DEFAULT_PERMITS_PER_SECOND = 1; // 令牌桶每秒填充速率


    @SneakyThrows
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        String remoteAddr = servletRequest.getRemoteAddr();
        RateLimiter rateLimiter = RATE_LIMITER_CACHE.get(remoteAddr, () -> RateLimiter.create(DEFAULT_PERMITS_PER_SECOND));
        if (rateLimiter.tryAcquire()) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            ((HttpServletResponse) servletResponse).setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            servletResponse.setContentType("application/json;charset=UTF-8");
            servletResponse.getWriter().write("Too Many Request!!!");
        }
    }
}

Spring Cloud Gateway实现限流

编写自定义的限流过滤器

参考zuul中限流方法,可以很容易的编写一个全局过滤器来进行限流,具体代码:

/**
 * 自定义过滤器
 *
 * @author yuanzhihao
 * @since 2022/4/27
 */
@Component
@Slf4j
@Order(-1)
public class RequestRateLimitFilter implements GlobalFilter {
    private static final Cache<String, RateLimiter> RATE_LIMITER_CACHE = CacheBuilder
            .newBuilder()
            .maximumSize(1000)
            .expireAfterAccess(1, TimeUnit.HOURS)
            .build();

    private static final double DEFAULT_PERMITS_PER_SECOND = 1; // 令牌桶每秒填充速率

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String remoteAddr = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
        RateLimiter rateLimiter = RATE_LIMITER_CACHE.get(remoteAddr, () -> RateLimiter.create(DEFAULT_PERMITS_PER_SECOND));
        if (rateLimiter.tryAcquire()) {
            return chain.filter(exchange);
        }
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        DataBuffer dataBuffer = response.bufferFactory().wrap("Too Many Request!!!".getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(dataBuffer));
    }
}

不过这种限流的粒度非常大,对于所有的请求都进行了限流,不能进行定制化的限流。之前博客里面整理过gatewayFilters局部过滤器的用法,这边可以参考进行限流过滤器的编写。

贴一下案例代码:

/**
 * 自定义局部限流
 *
 * @author yuanzhihao
 * @since 2022/4/27
 */
@Component
public class CustomRequestRateLimitGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomRequestRateLimitGatewayFilterFactory.Config> {
    public CustomRequestRateLimitGatewayFilterFactory() {
        super(Config.class);
    }

    private static final Cache<String, RateLimiter> RATE_LIMITER_CACHE = CacheBuilder
            .newBuilder()
            .maximumSize(1000)
            .expireAfterAccess(1, TimeUnit.HOURS)
            .build();

    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @SneakyThrows
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                String remoteAddr = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
                RateLimiter rateLimiter = RATE_LIMITER_CACHE.get(remoteAddr, () ->
                        RateLimiter.create(Double.parseDouble(config.getPermitsPerSecond())));
                if (rateLimiter.tryAcquire()) {
                    return chain.filter(exchange);
                }
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                DataBuffer dataBuffer = response.bufferFactory().wrap("Too Many Request!!!".getBytes(StandardCharsets.UTF_8));
                return response.writeWith(Mono.just(dataBuffer));
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("permitsPerSecond");
    }

    @Data
    public static class Config {
        private String permitsPerSecond; // 令牌桶每秒填充速率
    }
}

对应请求路由生效过滤器:

- id: server1
 uri: lb://eureka-server1
 predicates:
   - Path=/server1/hello
 filters:
   - CustomRequestRateLimit=1

Spring Cloud Gateway自实现的限流过滤器

spring cloud gateway里面也提供了一个自实现的限流过滤器org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory,这个过滤器里面有两个参数,一个是KeyResolver,这个参数可以动态的指定限流的一些key(个人理解,这边还是详细参考下官方文档~~~),比如这个key可以是访问的IP。

还有一个是RateLimiter,这个参数是具体的限流策略,在spring cloud gateway里面,它的默认实现是RedisRateLimiter,它采用的也是令牌桶算法

首先我们实现KeyResolver接口,指定限流的key是访问的IP地址:

/**
 * 根据ip地址进行限流
 * 
 * @author yuanzhihao
 * @since 2022/4/27
 */
@Component
public class HostAddrKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress());
    }
}

添加spring-boot-starter-data-redis-reactive依赖,使用RedisRateLimiter限流:

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

配置文件中添加redis和限流的配置信息:

redis:
  host: 127.0.0.1
  port: 6379
- id: server2
 uri: lb://eureka-server1
 predicates:
   - Path=/server1/twoDog
 filters:
   - name: RequestRateLimiter
     args:
      key-resolver: "#{@hostAddrKeyResolver}"
      redis-rate-limiter.replenishRate: 1 # 令牌桶填充的速率 秒为单位
      redis-rate-limiter.burstCapacity: 1 # 令牌桶总容量
      redis-rate-limiter.requestedTokens: 1 # 每次请求获取的令牌数

这边的参数表示填充的速率是1/s,桶的总容量也为1,每次请求获取一个令牌。也就是一秒只允许一次请求。测试生效:
在这里插入图片描述

结语

最后还是倾向于使用自定义的限流,他不需要引入redis组件,而且也可以自己重写响应到页面,更加灵活一点。

参考地址:https://docs.spring.io/spring-cloud-gateway/docs/3.0.4/reference/html/#the-requestratelimiter-gatewayfilter-factory

代码地址:https://github.com/yzh19961031/SpringCloudDemo/tree/main/gateway

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-29 11:57:32  更:2022-04-29 11:59:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:21:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码