也谈限流
限流的技术现在用的比较普遍了,网上一搜应该有大把的文章,为什么还来凑这个热闹呢,因为最近我们公司也在做限流,限流参考是以并发请求数作为限流参考的,即来一个请求计数器加1,请求结束对应计数器减1,如果计数器超过限流值则拒绝请求。
但有的同学不太理解,为什么以并发请求数,而不是TPS作为限流参考呢?
我们先看我们为什么要限流:
保护系统,防止系统雪崩;
限并发请求数和TPS都可以让达到目的。
我们再看下常用限流算法有哪些:
1、计数器算法
计数器算法是使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数。
打个比方,我们设的周期为1秒,设置1秒内最多能进来100个请求,如果在1秒内有第101个请求,则被限流。
2、滑动窗口算法
滑动窗口算法是将时间周期分为N个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。
这种算法的特点是 :当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。
3、漏桶算法
漏桶算法是访问请求到达时直接放入漏桶,如当前容量已达到上限(限流值),则进行丢弃。漏桶以固定的速率进行释放访问请求(即请求通过),直到漏桶为空。
4、令牌桶算法 令牌桶算法是程序以r(r=时间周期/限流值)的速度向令牌桶中增加令牌,直到令牌桶满,请求到达时向令牌桶请求令牌,如获取到令牌则通过请求,否则触发限流策略
其中1和2实现起来比较简单,3和4实现起来比较复杂,因为要动态做一些计算,时效性要求也比较高。
我们上面说的问题就是算法1,这个算法有个问题,即只统计请求的开始计数,这样会导致一个问题,即只能保证每个周期内只进来这么多请求,但无法保证服务器同时只处理这么多请求,什么意思呢,举个例子:
假设限流值设置的是1000,即1秒内最多只能处理1000个请求,我们假设每个请求处理的时间为50ms,假如在第一个周期的前960ms只进来100个请求,最后40ms内瞬间进入900个请求,而在第2个周期头5ms内进来800个请求,那在第2个周期的头10ms内服务器同时处理的请求有1700个请求,超出1000。
导致这个问题的关键是实现上只在请求进来时候进行了计数,而没有在请求结束的时候进行减掉计数器,导致计数有重叠。所以在实现上我们只要做到后面减数的这一点,则计数器大体上是比较准确的。
在实现上还要注意一个问题,如果用redis进行计数的话,伪代码如下:
incr url的对应的计数器;
if 计数器 == 1 then
设置计数器的过期时间为1s
else
end
可以看到这里可能有下面的因素导致不准:
1、程序在执行incr后挂了,那么过期时间就没有设置了,导致后面一直过期;解决方案是可以在服务端将incr和设置过期时间改造为原子命令,有兴趣的话后面可以单独一节讲解如何复合redis命令。
再回到上面的问题,并发和TPS哪个好呢,还是看实际的场景:
1、如果你的响应时间非常快,一般来说小于5ms,限流为并发请求数就没有太大意义了,我们在压测环境试过,用Lua写的接口,单接口在并发数限为1,TPS都能上千,这种场景下反而起不到限流的作用;
2、并发请求数实现会比较简单,它是基于瞬间压力来计算的,如果系统响应时间有很大变化的,如上100个请求的平均响应时间为50ms,而下100个的平均响应时间为100ms则比较适合这种情况;
而TPS是限制一个时间段的请求,效果会比较顺滑一些,如果系统响应时间比较固定,并且每个周期的请求数没有太多变化可以用这个;
另外TPS需要统计每秒的请求数,一般来说得用定时器实现,定时器在CPU压力比较大的情况下可能有延迟,会导致系统计数不准。
- 使用SQL Server Management Studio 2008 将数据库里的数据导成脚本
- jquery mobile 移动web(5)
- [C#6] 1-using static
- WordPress 中的 Debug 模式(调试模式)
- Windows Server 2008 R2 Server Core 的 Microsoft .NET Framework 4安装程序
- [C#6] 4-string 插值
- 使用API Key验证WCF Data Service
- WordPress By Example:一个WordPress 主题搜索引擎
- jquery mobile 移动web(4)
- [C#6] 3-null 条件运算符
- ServiceStack.Redis 使用教程
- WordPress 标签页面只有一篇文章时自动跳转到该文章
- OS X 上使用.NET开发应用程序
- [C#6] 2-nameof 运算符
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- WebLogic 10 容器通过JNDI切换数据源
- 2.2 spring5源码 -- ioc加载的整体流程
- Js 删除 指定Domin 指定 path 下的 cookie中指定的内容
- 适配器模式与装饰器模式的区别
- java堆内存详解
- springBoot 入门(一)—— 使用idea创建第一个springBoot项目
- “dddb超级”工具包——高效、快速开发JavaWeb项目后端结构
- 在Java Web中设计的编解码
- 怎么让用一行代码实现页面的定时强制刷新?脚本刷流量再也不用愁了!
- 什么是Javac
- springBoot 入门(二)—— 使用 spring.profiles.active来区分配置
- Kubernetes 1.19.0——deployment(2)
- JavaWeb新手进阶经典项目 & 半小时高效开发 & 海量知识点涵盖 (二)
- Java web 开发 Session超时设置
- JavaWeb第四讲 会话跟踪技术HttpSession、Cookie、url、隐藏表单域