我的编程空间,编程开发者的网络收藏夹
学习永远不晚

浅析Spring Cloud Gateway中的令牌桶限流算法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

浅析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中也是使用的令牌桶算法实现的限流

guava的文档:https://github.com/google/guava/wiki

Spring Cloud Gateway中的令牌桶

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

lua脚本地址:resources/META-INF/scripts/request_rate_limiter.lua

RedisRateLimiter:gateway/filter/ratelimit/RedisRateLimiter.java

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/rate
local ttl = math.floor(fill_time*2)
local last_tokens = tonumber(redis.call("get", tokens_key))
if last_tokens == nil then
  last_tokens = capacity
end
local last_refreshed = tonumber(redis.call("get", timestamp_key))
if last_refreshed == nil then
  last_refreshed = 0
end
local delta = math.max(0, now-last_refreshed)
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed then
  new_tokens = filled_tokens - requested
  allowed_num = 1
end
redis.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中的令牌桶限流算法的详细内容,更多关于pring Cloud Gateway令牌桶限流算法的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

浅析Spring Cloud Gateway中的令牌桶限流算法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

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

golang高并发系统限流策略漏桶和令牌桶算法源码分析

本篇内容主要讲解“golang高并发系统限流策略漏桶和令牌桶算法源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“golang高并发系统限流策略漏桶和令牌桶算法源码分析”吧!漏桶算法漏桶算法
2023-07-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录