分布式限流方案
https://www.cnblogs.com/jiangym/p/17473049.html
https://www.cnblogs.com/jiangym/p/17471590.html
常见限流
- 验证码
- 通常会设置多个维度的限流规则
- IP每秒的访问评率小于10、连接数小于5
- (怎么实现的?)
- 每台机器QPS最高1000,连接数最大保持200
- 怎么实现的?
- 整个服务器作为一个整体,设置更高的high-level限流规则
- Nginx限流
- 基于ID地址的限流方案
- 基于最大连接数的限流方案
- 基于Redis+Lua的分布式限流
- lua
- lua+redis
- 注解封装,自动以注解封装限流逻辑
- 黑白名单限流
- 网关层限流
- 网关层限流
- 网关层不像改个程序代码那么简单,和运维也相关
- Nginx
- gateway
- zuul
- 中间件限流
- 限流下沉到业务层,开发就可以自行控制
- redis
- 限流组件
- sentinel 阿里开源的
限流算法
常见就四种
- 令牌桶算法
- 漏桶算法
- 滑动窗口
- 计数器算法
令牌桶
token bucket
目前应用最广泛
分为
- 令牌
- 匀速发放 每秒100个请求 或者每分钟50个这种
- 匀速和非匀速的区别?
- 桶
- 生成之后放到桶中,到了额定容量,新的令牌就丢弃
- 请求获取令牌
- 请求获得令牌才能执行后续逻辑。
- 可以用缓冲队列存多余的令牌。
漏桶
Leaky Bucket
数据表放桶里。桶满了数据表将被丢弃。
区别
根据它们各自的特点不难看出来,这两种算法都有一个“恒定”的速率和“不定”的速率。
令牌桶是以恒定速率创建令牌,但是访问请求获取令牌的速率“不定”,反正有多少令牌发多少,令牌没了就干等。
而漏桶是以“恒定”的速率处理请求,但是这些请求流入桶的速率是“不定”的。
从这两个特点来说,漏桶的天然特性决定了它不会发生突发流量,就算每秒1000个请求到来,那么它对后台服务输出的访问速率永远恒定。
而令牌桶则不同,其特性可以“预存”一定量的令牌,因此在应对突发流量的时候可以在短时间消耗所有令牌,其突发流量处理效率会比漏桶高,但是导向后台系统的压力也会相应增多。
具体实现
Guava RateLimiter 当前服务器限流
非阻塞 令牌桶
非阻塞限流
同步阻塞限流
令牌不足的时候就阻塞住。
适用于 对单机资源敏感 读取的资源都是本地 不会有其他的组件
比如数据库,多个机器,就不适合这种的。
预热模型
@Override void doSetRate(double permitsPerSecond, double stableIntervalMicros) { double oldMaxPermits = maxPermits; // maxPermits表示令牌桶内最大容量,它由我们设置的预热时间除以稳定间隔获得 // 打个比方,如果stableIntervalMicros=0.1s,而我们设置的预热时间是2s // 那么这时候maxPermits就是2除以0.1=20 maxPermits = warmupPeriodMicros / stableIntervalMicros; // 这句不用解释了吧,halfPermits是最大容量的一半 halfPermits = maxPermits / 2.0; // coldIntervalMicros就是我们前面写到的3倍间隔,通过稳定间隔*3计算得出 // 稳定间隔是0.1,3倍间隔是0.2,那么平均间隔是0.2 double coldIntervalMicros = stableIntervalMicros * 3.0; // slope的意思是斜率,也就是前面我们图中预热阶段中画出的斜线(速率从稳定间隔向3x间隔变化的斜线) // 它的计算过程就是一个简单的求斜率公式 slope = (coldIntervalMicros - stableIntervalMicros) / halfPermits; // 计算目前令牌桶的令牌个数 if (oldMaxPermits == Double.POSITIVE_INFINITY) { // 如果令牌桶最大容量是无穷大,则设置当前可用令牌数为0 // 说实话这段逻辑没什么用 storedPermits = 0.0; } else { storedPermits = (oldMaxPermits == 0.0) ? maxPermits // 初始化的状态是3x间隔 : storedPermits * maxPermits / oldMaxPermits; } }
基于Nginx的IP限流
- 修改host文件
- 修改nginx,把host用的域名放到路由规则里 修改nginx.conf
- server
- server_name
- location
- 这里可以添加限流规则
-
- server
Nginx连接数的限制
前100M不限流:
基于Redis+Lua的分布式限流
redis基于内存的,并且线程安全,所以适合做限流
这种限流不是在Java内写的,比如获取令牌场景,会涉及发放令牌、查询令牌等操作,网络开销大。
所以直接通过Lua脚本方式,更好。
Lua
很小巧精致的语言,C语言编写的,源码两万行,Lua解释器才200K。
学这个语言不用面面俱到,因为细节很繁琐。
Redis中有Lua解释器,天生的原子性操作。有脚本预编译,直接调用编译好的脚本就行。
Lua 简单语法
结果是reject
Redis中执行Lua
redis中执行Lua
Lua脚本预导入Redis
检查预导入的脚本是否存在
清空预导入脚本
限流注解:
https://www.cnblogs.com/jiangym/p/17576108.html
如果限流服务挂了,怎么兜底?直接放开限流