限流算法
- 固定窗口
每秒的访问的QPS固定,如用redis+时间戳的方式
线程获得当前时间作为key,然后对key进行++,如果value>qps,则限流
生产环境中不会采用这个方式,因为粒度太粗了,比如我某个接口的QPS限流为10,但是我在1.5S和2.5S的请求数为6和7,难么实际上的QPS是>10的
- 滑动窗口
滑动窗口分为长窗口和短窗口
长窗口比如10S内qps100,但是无法抵御脉冲流量,比如1s的时候50qps,但是能让流量无损
短窗口用滑动窗口的方式来将窗口的切割为100ms,切割为10个窗口,这样能避免大部分的脉冲流量,然是会流量有损
- 滴水算法
以匀速的方式处理qps,无论qps多大,能很好的解决脉冲流量,但是无法快速相应
- 令牌算法
以匀速的方式加入令牌,qps有多少就从里面取多少令牌,有100令牌100个请求那么qps为100;有50个令牌100个请求那么qps为50
限流实战
生产环境里,一般不会用自己写的限流包,而是第三方包(方法成熟且经历大量实战)
推荐阿里巴巴开源的sentinel,有大厂背书,且资料较多(go的资料比较少)
sentinel中文文档
以下使用go+sentinel的方式来实现限流
- 初始化
func InitSentinel(c config.Config) error {
err := sentinel.InitWithConfigFile("./etc/user.yaml")
if err != nil {
// 初始化 Sentinel 失败
return err
}
_, err = flow.LoadRules([]*flow.Rule{
{
Resource: "user-limit",//定义资源名称
TokenCalculateStrategy: flow.Direct,//控制策略,Direct表示直接使用字段 Threshold 作为阈值;WarmUp表示使用预热方式计算Token的阈值。
ControlBehavior: flow.Reject,//表示流量控制器的控制策略;Reject表示超过阈值直接拒绝,Throttling表示匀速排队。
Threshold: 10,//周期内的访问量
StatIntervalInMs: 1000,//统计周期,1000表示10S
},
})
if err != nil {
// 加载规则失败,进行相关处理
return err
}
return nil
}
e, b := sentinel.Entry("user-limit", sentinel.WithTrafficType(base.Inbound))
//使用名为user-limit的资源进行限流
if b != nil {
return res, errors.New(b.Error())
}
defer e.Exit()
sentienl中台
安装jar包,然后以管理员身份启动指令可以开启中台,启动的时候注意端口是否被占用(8080端口经常被占用)
jar包地址
window环境下启动指令,可以写成一个bat文件
java -Dserver.port=8099 -Dcsp.sentinel.dashboard.server=localhost:8099 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.6.jar
sentienl中台启动后,可以监控运行中的程序的后端QPS情况,也可以配置限流内容
资源名是初始化的时候确认的,QPS是基于每秒的访问量,线程数表示这个接口只能N个线程访问,因为有些慢查询或则第三方的接口返回时间是不确定的,防止线程池爆了
快速失败使用的是滑动窗口算法
warmup是令牌算法,比如前N秒的qps为3,后面慢慢升级到10,是冷启动防止系统无法承受
排队等待是滴水算法