RuoYi-Vue 前后端分离版代码浅析-限流切面处理

932 阅读3分钟

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

前言

本节介绍RuoYi-Vue的模块中是如何进行数据权限切面的,对于网站而言,我们的限流是十分有必要的,可以将对应接口的访问流速进行一个限制,从而防止大流量进入将我们的程序拖挂。本文介绍的Ruoyi的限流处理是使用的RedisTempalte来进行的,采用的是最简单的统计计数方法,利用了Redis的incr和expire的api实现。

常见的限流方法

除了我们的统计计数法,还有漏桶算法和令牌桶算法。

统计计数法

主要用来限制总并发数,只要总请求数和一定时间段的请求数达到设定阈值,则进行限流,不是平均速率限流

漏桶算法

可用于流量整形和流量控制,固定容量的漏桶,每分钟处理数据的数量是一致的,放多少数据进来都可以,如果没有数据,那么不处理,如果数据超过桶的容量,丢弃

令牌桶算法

令牌桶算法,是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌。桶满时新添加的令牌被丢弃或拒绝。 当处理数据时需要消耗令牌,如果没有令牌时不会处理此数据(数据可能等待或丢弃)

漏桶和令牌桶

令牌桶是按照固定速率往桶中添加令牌,看桶中令牌是否足够来决定能否处理请求数据,当令牌数减为零时,则拒绝新的请求。 漏桶则是按照常量限定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶最大容量之后,则新流入的请求被拒绝。 令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,或4个令牌),并允许一定程度的突发流量。 漏桶限制的是常量流出速率(即流出速率是一个固定常量值,只能小于这个固定常量值),从而限制住突发流入速率。 令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率。 两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。

Ruoyi的实现

Ruoyi使用的是统计计数法,首先是在RedisConfig类中放入了限流的lua脚本

@Bean
public DefaultRedisScript<Long> limitScript()
{
    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
    redisScript.setScriptText(limitScriptText());
    redisScript.setResultType(Long.class);
    return redisScript;
}

/**
 * 限流脚本
 */
private String limitScriptText()
{
    return "local key = KEYS[1]\n" +
            "local count = tonumber(ARGV[1])\n" +
            "local time = tonumber(ARGV[2])\n" +
            "local current = redis.call('get', key);\n" +
            "if current and tonumber(current) > count then\n" +
            "    return tonumber(current);\n" +
            "end\n" +
            "current = redis.call('incr', key)\n" +
            "if tonumber(current) == 1 then\n" +
            "    redis.call('expire', key, time)\n" +
            "end\n" +
            "return tonumber(current);";
}

在对应的注解中通过传入参数key的名称,限制次数,过期时间,来进行统计。

Long number = redisTemplate.execute(limitScript, keys, count, time);

首先获得对应的key当前存储的值,如果当前存储的值大于限制次数,直接返回,如果不大于,那么此值调用incr命令去做加1操作。 如果加1之后current为1,给它设置一个过期时间,从而保证它在限制时间后可以被销毁,从而进行一个新的统计。