限流技术的应用场景

业务场景

淘宝双十一

如果你在双十一0点0分之后,购物的时候遇到这个页面,那么亲,你被限流了,必须排到等待

天猫限流

用户洪峰

特点:

  • 允许访问的速率
  • 爆发量
  • 爆发间隔时间

为了提升用户体验,需要支持爆发量,所以采用令牌桶算法

令牌桶算法

允许最大的访问速率:b+r
爆发持续时间:T=b/(m-2r)
爆发量:L=T*r

回调洪峰

特点:

  • 有间隔频率
  • 每次调用量大
  • 允许有延时

采用漏桶算法

漏桶算法

限流框架

分成几个主要模块

  • 监控模块

    收集数据、实时监控和反馈分析

  • 决策模块

    什么场景使用什么限流方式;比如,用户洪峰采用令牌桶算法,

    系统回调使用漏桶算法

  • 规则变更模块

    动态调整令牌桶容量和产生令牌的速率

  • 限流模块

    如何处理被限流的请求,是排队还是丢弃

限流框架的处理流程

处理流程

小米秒杀

TODO

分层架构

Nginx限流

Nginx限流模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
vi /etc/nginx/conf.d/default.conf
......
upstream myapp {
server 127.0.0.1:8081;
}
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/s;
server {
server_name _;
location / {
proxy_pass http://myapp;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
location /account/login/ {
# apply rate limiting
limit_req zone=login burst=5 nodelay;
# boilerplate copied from location /
proxy_pass http://myapp;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}

参数说明

  • $binary_remote_addr

    采用二进制表示访问的IP地址

  • zone=login:10m

    表示定义了一个名为login的内存共享区,其占用内存大小为10M。

    10M的内存可以存放16W个二进制表示的IP地址。

  • rate=1r/s

    就是QPS;对同一个IP地址而言,速率被限制为每秒1次请求

  • burst

    如果同一个IP访问的请求超过rate后,没有超过桶上限burst的请求会被放入桶中,等待被处理。

    这个例子中桶的容量是5。

  • nodelay

    单个IP的在某时间段内平均QPS未超过设定的rate,那么桶中的请求会立即被处理,

    此时的瞬间QPS高于设定的rate;如果在某时间段内平均的QPS超过rate,那么超过

    rate的请求会直接被拒绝,直接返回503

从示例配置信息中可以看出:

对访问/account/login/的请求根据访问的IP进行了限速,QPS是10;

对于同一个IP地址只允许每秒10个请求访问该URL;

如果请求量大于QPS(10),会立即返回503(因为配置了参数nodelay);

如果没有配置参数nodelay,那么超过QPS访问量的请求会先积压到桶中(可以积压5个),

如果桶满了,就返回503。

nginx限流模块默认使用的是漏桶算法,而当配置了nodelay时采用的是令牌算法,此时允许burst请求

重点说明一下对burst+nodelay的理解

1
2
3
4
5
6
7
8
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
......
location /account/login/ {
# apply rate limiting
limit_req zone=login burst=15 nodelay;
}
时间点 并发请求量 拒绝的请求次数 成功的请求次数
10:00:01 15 0 15
10:00:15 15 0 15
10:00:30 15 0 15
10:00:45 15 0 15
11:00:01 15 0 15
11:00:02 15 14 1
11:00:03 15 14 1
11:00:04 15 14 1

针对这个case的理解:

(1)在[10:00:01到10:00:45]期间,每隔15秒会有15个并发请求,虽然在这些时间点上并发

请求大于rate,但在每个15秒内其QPS是1,依旧未超过rate,所以每次的15个并发请求能够

被成功处理

(2)在[11:00:01到11:00:05]期间,每秒15个并发请求,在11:00:01时,请求量未超过burst,

并且由于配置了nodelay,所以当时15个请求立即被执行了;而在其他的时间点上QPS大于

rate,所以超出rate的请求直接被拒绝

Nginx+lua

REDIS

http://redis.io/commands/INCR#pattern-rate-limiter
https://github.com/UsedRarely/spring-rate-limit
https://github.com/colley/spring-ratelimiter
https://github.com/nlap/dropwizard-ratelimit
https://github.com/sudohippie/throttle
https://github.com/coveo/spillway
https://github.com/marcosbarbero/spring-cloud-starter-zuul-ratelimit

API 调用次数限制实现

Java Rate-Limiting API

ZK

Distributed Atomic Long

Shared Semaphore

HTTP&DUBBO&MQ

filter+

DB

淘宝双十一秒杀业务

秒杀特点:瞬时并发高、数据一致性高、热点更新频率高

大量更新DB中的同一条记录时,会产生锁等待,导致DB性能急剧下降

当大量的并发更新同一条记录时,使用排队的方式来保证高并发下热点记录更新依然能保持

较好的性能,为threads_running设置一个硬上线,当并发超过此值是,拒绝执行sql,

保护MySQL,我们将这个称之为高水位限流,这样就给数据库加上了一层限流的功能,使得

数据库不被瞬间的高爆发请求打爆。

高水位限流实现:

监控系统status变量threads_running,当满足拒绝条件,拒绝执行sql,返回用户:

MySQL Server is too busy,判断逻辑在dispatch_command中,sql解析之后。

增加的系统variables:

  1.threads_running_ctl_mode: 限流的sql类型,有两个取值:[ALL | SELECTS],
  
  默认SELECTS,设置为ALL需谨慎。
  
  2.threads_running_high_watermark: 限流水位值,只有threads_running
  
  超过此值才会触发,默认值为max_connections,当set global 
  
  threads_running_high_watermark=0时自动设置为max_connections

拒绝必要条件:

 1.threads_running超过threads_running_high_watermark
 
 2.threads_running_ctl_mode与sql类型相符

以下情况不拒绝:

 1.用户具有super权限
 
 2.sql所在事务已经开启
 
 3.sql为commit/rollback

参考资料

阿里双11系统管控调度架构与实践.pdf

Nginx限流模块