java 网关限流_网关限流总结

本文介绍了Java网关限流的常用算法,包括计数器、漏桶和令牌桶算法,并详细分析了Spring Cloud Gateway和Spring Cloud Zuul的限流实现。通过对不同算法的探讨,揭示了它们的优缺点,如计数器算法可能导致的“突刺现象”,漏桶算法的平滑处理,以及令牌桶算法在应对突发流量上的优势。同时,文章还讨论了Guava RateLimiter在平滑限流上的应用,并提出了一种自研限流方案,旨在提高网关吞吐量并确保线程安全性。
摘要由CSDN通过智能技术生成

1. 常用限流算法

1.1 计数器算法

统计一段时间内允许通过的请求数。比如 qps为100,即1s内允许通过的请求数100,每来一个请求计数器加1,超过100的请求拒绝、时间过1s后计数器清0,重新计数。这样限流比较暴力,如果前10ms 来了100个请求,那剩下的990ms只能眼睁睁看着请求被过滤掉,并不能平滑处理这些请求,容易出现常说的“突刺现象”。

计数器算法流程图如下

d3390fa7d34b3b1e359b3a21b21902eb.png

1.2漏桶算法

请求到来时先放入漏桶中,漏桶再以匀速放行请求,如果进来请求超出了漏桶的容量时,则拒绝请求,这样做虽然能够避免“突刺现象”,但是过于平滑并不能应对短时的突发流量。

具体实现可采取将到来的请求放入队列中,再另起线程从队列中匀速拿出请求放行。

1.3令牌桶算法

假设有个桶,并且会以一定速率往桶中投放令牌,每次请求来时都要去桶中拿令牌,如果拿到则放行,拿不到则进行等待直至拿到令牌为止,比如以每秒100的速度往桶中投放令牌,令牌桶初始化一秒过后桶内有100个令牌,如果大量请求来时会立即消耗完100个令牌,其余请求进行等待,最终以匀速方式放行这些请求。此算法的好处在于既能应对短暂瞬时流量,又可以平滑处理请求。

令牌桶限流流程图

4053f9c0275af3a61d8cf48c59a247ed.png

2. 网关限流原理解析

2.1 spirng cloud gateway网关限流原理解析

核心限流类:org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter

核心方法如下:

public Mono isAllowed(String routeId, String id) {

if (!this.initialized.get()) {

throw new IllegalStateException("RedisRateLimiter is not initialized");

}

Config routeConfig = loadConfiguration(routeId);

//令牌桶平均投放速率

int replenishRate = routeConfig.getReplenishRate();

//桶容量

int burstCapacity = routeConfig.getBurstCapacity();

try {

//获取限流key

List keys = getKeys(id);

//组装lua脚本执行参数,第一个参数投放令牌速率、第二个参数桶的容量、第三个参数当前时间戳,第四个参数需要获取的令牌个数

List scriptArgs = Arrays.asList(replenishRate + "",

burstCapacity + "", Instant.now().getEpochSecond() + "", "1");

//通过lua脚本与redis交互获取令牌,返回数组,数组第一个元素代表是否获取成功(1成功0失败),第二个参数代表剩余令牌数

Flux> flux = this.redisTemplate.execute(this.script, keys,

scriptArgs);

//如果获取令牌异常,默认设置获取结果【1、-1】,顾默认获取令牌成功、剩余令牌-1,不做限流控制

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;

});

}

catch (Exception e) {

log.error("Error determining if user allowed from redis", e);

}

return Mono.just(new Response(true, getHeaders(routeConfig, -1L)));

}

gateway限流lua脚本实现如下:

b481a24ffd74a00564ea44fb294a849f.png

lua脚本分析及备注如下:

--令牌桶剩余令牌数key

local tokens_key = KEYS[1]

--令牌桶最后填充时间key

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)

--获取redis中剩余令牌数

local last_tokens = tonumber(redis.call("get", tokens_key))

if last_tokens == nil then

last_tokens = capacity

end

--获取redis中最后一次更新令牌的时间

loca

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值