千家信息网

Spring Cloud Gateway中的令牌桶限流算法实例分析

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,这篇"Spring Cloud Gateway中的令牌桶限流算法实例分析"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能
千家信息网最后更新 2025年01月18日Spring Cloud Gateway中的令牌桶限流算法实例分析

这篇"Spring Cloud Gateway中的令牌桶限流算法实例分析"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"Spring Cloud Gateway中的令牌桶限流算法实例分析"文章吧。

    前言

    在一个分布式高并发的系统设计中,限流是一个不可忽视的功能点。如果不对系统进行有效的流量访问限制,在双十一和抢票这种流量洪峰的场景下,很容易就会把我们的系统打垮。而作为系统服务的卫兵的网关组件,作为系统服务的统一入口,更需要考虑流量的限制,直接在网关层阻断流量比在各个系统中实现更合适。Spring Cloud Gateway的实现中,就提供了限流的功能,下面主要分析下Spring Cloud Gateway中是如何实现限流功能的。

    回顾限流算法

    限流的实现方式有多种,下面先回顾下几种常见的实现算法

    计数器/时间窗口法

    这种限流算法最简单,也是最容易实现的,通过在单位时间内设置最大访问数就可以达到限流的目的。比如某个系统能够承载的一般qps为60,那我们就可以使用计算器法,在单位时间一秒内,限制接口只能被访问60次即可。但是这个算法实现,正如其功能描述一样,有个缺陷,假如在时间窗的前1%的时间内流量就达到顶峰了,那么在时间窗内还有99%的时间系统即使能够继续提供服务,还是会被限流算法的这种缺陷阻断在门外,这种缺陷也被称为"突刺效应"

    漏桶法

    漏桶法不同于计算器法,它有效的避免了计数器法限流的"突刺效应"缺陷,实现也不复杂,通过固定大小的队列+定时取队列元素的方式即可实现。如其名漏桶,就像一个盛水的容器,漏桶法只限制容器出水的速率,当进水的速率过大时,将会填满容器造成溢出,溢出部分的流量也就是拒绝的流量。比如,容器大小为100,出水速率为每秒10/s,当桶为空时,最大的流量可以到达100/s,但是即使这样,受限于固定的流出速率,后端处理的也只能是最大每秒10个,其余的流量都会被缓冲在漏桶中。这个也这是漏桶法的缺陷,没法真正处理突发的流量洪峰,效率不高。

    令牌桶法

    令牌桶法也是基于桶的原型,但是和漏桶算法截然不同的时,没有出水口。令牌桶通过令牌的产生速率+令牌桶的容积来控制流量,有效的解决了漏桶效率不高的问题。如,容积为100的桶,令牌产生速率为50/s,那么就代表当桶中令牌已满的时候,最大能够承载100的流量,后面如果流量一直居高不下,也会以每秒50个流量的速度恒速处理请求。令牌桶的这种特性有效的处理了洪峰流量也能做到不被洪峰压垮,是目前限流比较常见的实现方法。比较著名的实现有谷歌guava中的RateLimiter。然后下面将要分析的Spring Cloud Gateway中也是使用的令牌桶算法实现的限流

    Spring Cloud Gateway中的令牌桶

    Spring网关中是基于令牌桶+redis实现的网关分布式限流,具体的实现见下面两个代码:

    try {                        Listkeys = getKeys(id);                        // The arguments to the LUA script. time() returns unixtime in seconds.                        ListscriptArgs = Arrays.asList(replenishRate + "",                                        burstCapacity + "", Instant.now().getEpochSecond() + "", "1");                        // allowed, tokens_left = redis.eval(SCRIPT, keys, args)                        Fluxflux = this.redisTemplate.execute(this.script, keys,                                        scriptArgs);                        // .log("redisratelimiter", Level.FINER);                        return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L)))                                        .reduce(new ArrayList(), (longs, l) -> {                                                longs.addAll(l);                                                return longs;                                        }).map(results -> {                                                boolean allowed = results.get(0) == 1L;                                                Long tokensLeft = results.get(1);                                                Response response = new Response(allowed,                                                                getHeaders(routeConfig, tokensLeft));                                                if (log.isDebugEnabled()) {                                                        log.debug("response: " + response);                                                }                                                return response;                                        });                }

    上面博主截取了Spring网关限流部分的关键代码,可以看到,最关键的地方在于,使用reids执行了一段lua脚本,然后通过返回值【0】是否等于1来判断本次流量是否通过,返回值【1】为令牌桶中剩余的令牌数。就上面这段代码没有看到任何令牌桶算法的影子对吧,所有的精华实现都在lua脚本里面,这个脚本最初是由Paul Tarjan分享出来的,脚本如下:

    local tokens_key = KEYS[1]local timestamp_key = KEYS[2]local rate = tonumber(ARGV[1])local capacity = tonumber(ARGV[2])local now = tonumber(ARGV[3])local requested = tonumber(ARGV[4])local fill_time = capacity/ratelocal ttl = math.floor(fill_time*2)local last_tokens = tonumber(redis.call("get", tokens_key))if last_tokens == nil then  last_tokens = capacityendlocal last_refreshed = tonumber(redis.call("get", timestamp_key))if last_refreshed == nil then  last_refreshed = 0endlocal delta = math.max(0, now-last_refreshed)local filled_tokens = math.min(capacity, last_tokens+(delta*rate))local allowed = filled_tokens >= requestedlocal new_tokens = filled_tokenslocal allowed_num = 0if allowed then  new_tokens = filled_tokens - requested  allowed_num = 1endredis.call("setex", tokens_key, ttl, new_tokens)redis.call("setex", timestamp_key, ttl, now)return { allowed_num, new_tokens }

    下面逐行分析下这段脚本。首先解释下,从应用中入参进来的这几个属性的具体含义:

    tokens_key:当前限流的标识,可以是ip,或者在spring cloud系统中,可以是一个服务的serviceID

    timestamp_key:令牌桶刷新的时间戳,后面会被用来计算当前产生的令牌数

    rate :令牌生产的速率,如每秒产生50个令牌

    capacity :令牌桶的容积大小,比如最大100个,那么系统最大可承载100个并发请求

    now :当前时间戳

    requested:当前请求的令牌数量,Spring Cloud Gateway中默认是1,也就是当前请求

    主要逻辑分析

    -- 计算填满桶需要多长时间
    -- 得到填满桶的2倍时间作为redis中key时效的时间,避免冗余太多无用的key
    -- 这里和令牌桶的实现没有太大的关系
    -- 获取桶中剩余的令牌,如果桶是空的,就将他填满
    -- 获取当前令牌桶最后的刷新时间,如果为空,则设置为0
    -- 计算最后一次刷新令牌到当前时间的时间差
    -- 计算当前令牌数量,这个地方是最关键的地方,通过剩余令牌数 + 时间差内产生的令牌得到当前总令牌数量
    -- 设置标识allowad接收当前令牌桶中的令牌数是否大于请求的令牌结果
    -- 设置当前令牌数量
    -- 如果allowed为true,则将当前令牌数量重置为通中的令牌数 - 请求的令牌数,并且设置allowed_num标识为1
    -- 将当前令牌数量写回到redis中,并重置令牌桶的最后刷新时间
    -- 返回当前是否申请到了令牌,以及当前桶中剩余多少令牌

    以上就是关于"Spring Cloud Gateway中的令牌桶限流算法实例分析"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。

    令牌 时间 流量 算法 系统 分析 速率 最大 数量 内容 缺陷 网关 脚本 有效 功能 容器 洪峰 剩余 处理 服务 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 erf服务器维修项目报价 网络信息安全和数据库建设 服务器的充电桩都适用吗 数据库一次只能连接一个用户 我的世界多人游戏怎么加入服务器 华南理工网络安全考研 软件开发过程图 更换备调前置服务器的安全措施 天下三再战今朝服务器没玄修么 入网产品网络安全检测 辽宁服务器电源制造厂 数据库复制到另一个视图 网络安全类风险 魔题库软件开发 电信部门的网络安全制度 网安大队检查企业网络安全 亳州网络安全局 软件开发票税率怎么改 山西oa软件开发服务至上 广东省服务器托管云服务器 小学网络安全应急预案模板 保障网络安全维护国家安全 捕鱼软件开发流程 入网产品网络安全检测 网络安全与电子商务知识点 数据库的四个主要特点是什么 数据库查出某列对应密码内容 服务器地址北京交通大学 开源监控oracle数据库 吉尼斯世界纪录视频软件开发
    0