Spring Cloud Gateway 限流

news2024/11/25 2:23:25

在高并发的应用中,限流是一个绕不开的话题。限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击。

一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如 nginx 的 limit_conn 模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(如 Guava 的 RateLimiter、nginx 的 limit_req 模块,限制每秒的平均速率);其他还有如限制远程接口调用速率、限制 MQ 的消费速率。另外还可以根据网络连接数、网络流量、CPU 或内存负载等来限流。

本文详细探讨在 Spring Cloud Gateway 中如何实现限流。

# 限流算法

做限流 (Rate Limiting/Throttling) 的时候,除了简单的控制并发,如果要准确的控制 TPS,简单的做法是维护一个单位时间内的 Counter,如判断单位时间已经过去,则将 Counter 重置零。此做法被认为没有很好的处理单位时间的边界,比如在前一秒的最后一毫秒里和下一秒的第一毫秒都触发了最大的请求数,也就是在两毫秒内发生了两倍的 TPS。

常用的更平滑的限流算法有两种:漏桶算法和令牌桶算法。很多传统的服务提供商如华为中兴都有类似的专利,参考采用令牌漏桶进行报文限流的方法。

# 漏桶算法

漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。

可见这里有两个变量,一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发(burst)到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。

# 令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解。随着时间流逝,系统会按恒定 1/QPS 时间间隔(如果 QPS=100,则间隔是 10ms)往桶里加入 Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了。新请求来临时,会各自拿走一个 Token,如果没有 Token 可拿了就阻塞或者拒绝服务。

令牌桶的另外一个好处是可以方便的改变速度。一旦需要提高速率,则按需提高放入桶中的令牌的速率。一般会定时(比如 100 毫秒)往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量。 

Leakly Bucket vs Token Bucket

# 限流实现(Redis)

1.引入jar包

        <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</artifactId>
        </dependency>

 2.编写配置文件

spring:
  application:
    name: gateway-9205
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8847
    gateway:
      routes:
        - id: user-provider-9206
          uri: lb://user-provider-9206
          predicates:
            - Path=/user/**
          filters:
            - RewritePath=/user(?<segment>/?.*), $\{segment}
            - name: RequestRateLimiter
              args:
                # 如果返回的key是空的话,则不进行限流
                deny-empty-key: false
                # 每秒产生多少个令牌
                redis-rate-limiter.replenishRate: 1
                # 1秒内最大的令牌,即在1s内可以允许的突发流程,设置为0,表示阻止所有的请求
                redis-rate-limiter.burstCapacity: 1
                # 每次请求申请几个令牌
                redis-rate-limiter.requestedTokens: 1
  redis:
    host: 192.168.7.1
    database: 12
    port: 6379
    password: 123456

server:
  port: 9205
debug: true

3.网关正常响应

 

4.网关限流响应

 

自定义限流算法和限流key 

1.自定义限流key

编写一个类实现 KeyResolver 接口即可。
 

package com.huan.study.gateway;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
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.util.Optional;

/**
 * 限流的key获取
 *
 * @author huan.fu 2021/9/7 - 上午10:25
 */
@Slf4j
@Component
public class DefaultGatewayKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // 获取当前路由
        Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);

        ServerHttpRequest request = exchange.getRequest();
        String uri = request.getURI().getPath();
        log.info("当前返回的uri:[{}]", uri);

        return Mono.just(Optional.ofNullable(route).map(Route::getId).orElse("") + "/" + uri);
    }
}

配置文件中的写法(部分)

spring:
  cloud:
    gateway:
      routes:
        - id: user-provider-9206
          filters:
            - name: RequestRateLimiter
              args:
                # 返回限流的key
                key-resolver: "#{@defaultGatewayKeyResolver}"

2.自定义限流算法

编写一个类实现 RateLimiter ,此处使用内存限流

package com.huan.study.gateway;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter;
import org.springframework.cloud.gateway.support.ConfigurationService;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
 * @author huan.fu 2021/9/7 - 上午10:36
 */
@Component
@Slf4j
@Primary
public class DefaultGatewayRateLimiter extends AbstractRateLimiter<DefaultGatewayRateLimiter.Config> {

    /**
     * 和配置文件中的配置属性相对应
     */
    private static final String CONFIGURATION_PROPERTY_NAME = "default-gateway-rate-limiter";

    private RateLimiter rateLimiter = RateLimiter.create(1);

    protected DefaultGatewayRateLimiter(ConfigurationService configurationService) {
        super(DefaultGatewayRateLimiter.Config.class, CONFIGURATION_PROPERTY_NAME, configurationService);
    }

    @Override
    public Mono<Response> isAllowed(String routeId, String id) {
        log.info("网关默认的限流 routeId:[{}],id:[{}]", routeId, id);

        Config config = getConfig().get(routeId);

        return Mono.fromSupplier(() -> {
            boolean acquire = rateLimiter.tryAcquire(config.requestedTokens);
            if (acquire) {
                return new Response(true, Maps.newHashMap());
            } else {
                return new Response(false, Maps.newHashMap());
            }
        });
    }

    @Getter
    @Setter
    @ToString
    public static class Config {
        /**
         * 每次请求多少个 token
         */
        private Integer requestedTokens;
    }
}

配置文件中的写法(部分)

spring:
  cloud:
    gateway:
      routes:
        - id: user-provider-9206
          filters:
            - name: RequestRateLimiter
              args:
                # 自定义限流规则
                rate-limiter: "#{@defaultGatewayRateLimiter}"

注意⚠️: 这个类需要加上 @Primary 注解。

3.配置文件中的写法

spring:
  application:
    name: gateway-9205
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8847
    gateway:
      routes:
        - id: user-provider-9206
          uri: lb://user-provider-9206
          predicates:
            - Path=/user/**
          filters:
            - RewritePath=/user(?<segment>/?.*), $\{segment}
            - name: RequestRateLimiter
              args:
                # 自定义限流规则
                rate-limiter: "#{@defaultGatewayRateLimiter}"
                # 返回限流的key
                key-resolver: "#{@defaultGatewayKeyResolver}"
                # 如果返回的key是空的话,则不进行限流
                deny-empty-key: false
                # 限流后向客户端返回的响应码429,请求太多
                status-code: TOO_MANY_REQUESTS
                # 每次请求申请几个令牌  default-gateway-rate-limiter 的值是在 defaultGatewayRateLimiter 中定义的。
                default-gateway-rate-limiter.requestedTokens: 1
server:
  port: 9205
debug: true

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/499538.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】类和对象(初阶认识)#中篇#

上篇讲到对象的实例化 这里我们接着来探讨对象 目录 类域及成员函数在类域外的声明方法 内联 构造函数 先来看前三点&#xff1a; 无参调用格式 第四点函数重载 最后一点&#xff1a;没写构造时 自动生成 默认构造 并调用 《坑和补丁篇》 默认构造 析构函…

SETUNA2简介、下载和使用方法(截图贴图工具)

如果你在寻找一个可以截图并将截图置顶显示在桌面的工具&#xff0c;那么本文介绍的工具可以满足你的需求&#xff0c;但是我还是建议你移步&#xff1a; Snipaste介绍、安装、使用技巧&#xff08;截图贴图工具&#xff09;_西晋的no1的博客-CSDN博客 &#xff0c;Snipaste工具…

Illustrator如何使用符号与图表之实例演示?

文章目录 0.引言1.使用Microsoft Excel数据创建图表2.修改图表图形及文字 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对Illustrator进行了学习&#xff0c;本文通过《Illustrator CC2018基础与实战》及其配套素材结合网上相关资料进行学习笔记总结&#xff0c;本文…

校园网自动登陆(河南科技学院)

1. 介绍 河南科技学院校园网自动登陆&#xff08;新乡的很多系统相似&#xff0c;可能也可以用&#xff1f;&#xff09;&#xff0c;java版。可以实现电脑&#xff0c;路由器&#xff0c;软路由的自动认证wifi,后续会上传docker版本的。 源码地址 github&#xff1a;https://…

C嘎嘎的运算符重载基础教程以及遵守规则【文末赠书三本】

博主名字&#xff1a;阿玥的小东东 大家一起共进步&#xff01; 目录 基础概念 优先级和结合性 不会改变用法 在全局范围内重载运算符 小结 本期送书&#xff1a;盼了一年的Core Java最新版卷Ⅱ&#xff0c;终于上市了 基础概念 运算符重载是通过函数重载实现的&#xf…

visual studio code安装c语言编译环境

目录 &#xff08;一&#xff09;Windows下安装GCC&#xff0c;下载并安装MinGW 安装MinGW 配置GCC环境变量 电脑使用CMD命令行输入 gcc -v ,查看gcc当前版本号以此判断gcc是否安装成功​编辑 &#xff08;一&#xff09;Windows下安装GCC&#xff0c;下载并安装MinGW 下载…

索引合并,能不用就不要用吧!

文章目录 1. 问题重现2. 索引合并2.1 Using intersect(...)2.2 Using union(...)2.3 Using sort_union(...)2.4 索引合并原理 3. 索引合并的问题 在前面的文章中&#xff0c;松哥和小伙伴们分享了 MySQL 中&#xff0c;InnoDB 存储引擎的数据结构&#xff0c;小伙伴们知道&…

HTB-OpenKeyS

HTB-OpenKeyS 信息收集80端口立足于JenniferJennifer -> root 信息收集 80端口 对其进行简单的SQL注入测试和NoSQL注入测试后进行目录扫描。 auth.swp文件内容如下&#xff1a; 代码不是很完整&#xff0c;只能大致了解意思&#xff08;请原谅我脑子抽了没注意是个swp交换…

USB 连接检测

文章目录 连接检测连接状态的检测带 Vbus 检测功能的 USB 设备不带 Vbus 检测功能的 USB 设备 连接前的初始化设备端主机端 建立连接过程手册规定检测时间及电平标准 连接检测 USB 协议支持热插拔的特性决定了 USB 主机必须能够动态地检测 USB 设备的连接和断开&#xff0c;这…

linux【网络编程】之网络基础

linux【网络编程】之网络基础 一、网络协议与协议分层1.1 为什么要分层1.2 OSI七层模型1.3 TCP/IP五层(或四层)模型 二、网络传输流程2.1 了解局域网2.2 同一网段内的两台主机进行文件传输2.3 跨网段的主机的文件传输 三、数据包封装和分用四、网络中的地址管理4.1 IP地址4.2 M…

【Simulink】0基础入门教程 P1 搭建自己的第一个模型 实现加减乘除四则运算

目录 工作路径的设置&#xff1a; Simulink的两种打开方式 模块的基本操作 建立一个新的空白模型&#xff0c;创建模型 加减乘除模块的名称 模块之间连线的两种方法 显示模块 Display 搭建子系统subsystem 将加法模块Add更改为多输入模块 本文记录Simulink学习&#x…

[零刻]EQ12EQ12Pro调整风扇转速教程

调整 CPU 风扇转速可以有不同的用途&#xff0c;具体取决于您的计算机和使用情况。 降低噪音&#xff1a;如果您的风扇的噪音很大&#xff0c;可以通过降低 CPU 风扇的转速来减少噪音。这可以通过在 BIOS 或中设置 CPU 风扇转速控制来实现。 提高性能&#xff1a;如果您的计算…

OpenVINO 2022.3实战一:Window 10 环境下用 OpenVINO 2022.3部署yolov5 7.0

Window 10 环境下用 OpenVINO 2022.3部署yolov5_7.0 1 下载并解压 OpenVINO Runtime OpenVINO™ Runtime 2022.3 以压缩包 (OpenVINO Archives) 的形式提供。 下载地址&#xff1a; storage.openvinotoolkit.org 下载后解压到 C:\Intel\openvino_2022.3.0 配置环境&#xff…

5月5日 8H25min|5月6日 3H10min|时间轴复盘

7:30-8:00 起床洗漱吃饭 8:00-8:30 背书 【30min】 8:30-9:40 对话单词 【1h10min】 9:45-11:30 听力精听 【2h-15min】 11:30-12:10 吃午饭吃水果 12:10-12:50 继续吃饭之前没完成的 【40min】 13:00-14:30 健身 14:35-14:43 语法 【1…

asdfghasdfghjkl

PDL1检测&#xff1a; 肿瘤细胞高表达PD-L1分子&#xff0c;与肿瘤部位浸润T淋巴细胞表面的PD-1分子结合后&#xff0c;抑制T细胞活性&#xff0c;实现肿瘤的免疫逃避。而目前PD-1/PD-L1抑制剂均是检测PD-L1的表达。 目前在NSCLC治疗中&#xff0c;对于每个PD-1/PD-L1抑制剂&a…

【Hive大数据】Hive分区表与分桶表使用详解

目录 一、分区概念产生背景 二、分区表特点 三、分区表类型 3.1 单分区 3.2 多分区 四、动态分区与静态分区 4.1 静态分区【静态加载】 4.1.1 操作演示 4.2 多重分区 4.2.1 操作演示 4.3 分区数据动态加载 4.3.1 分区表数据加载 -- 动态分区 4.3.2 操作演示 五、…

mysql事务及搜索引擎

mysql事务后半部分 加快查询速度索引会自动排序&#xff0c;&#xff08;升序&#xff09; select * from t1&#xff1b;全盘扫描 where可以索引查找show create table 索引是一个排序的列表&#xff0c;包含字段值和相应行数据的物理地址 事务是一种机制&#xff0c;一个…

Misc小总结

Misc分类 个人认为Misc中的题目可分为七大类&#xff0c;图片隐写&#xff0c;音视频隐写&#xff0c;其它隐写(PPT、word文档等隐写)&#xff0c;压缩包破解&#xff0c;流量分析&#xff0c;取证&#xff0c;编码或密码。这里面涉及的知识点当然是很多的&#xff0c;有很多你…

大学毕业设计使用python制作

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

Python标准数据类型-字符串常用方法(上)【文末送书】

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1 &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;零基础入门篇 &#x1f4ac;个人格言&#xff1a;不断的翻越一座又一座的高山…