1 常用限流方式
2 容器限流示例: Nginx限流
2.1 控制速率:
2.2 控制并发数
3服务端限流
3.1 时间窗口算法
时间窗口算法又分为固定时间窗口和滑动时间窗口。
固定时间窗口是指固定时间段内, 使用计数器记录这段时间内接收的请求, 每接收一次请求就让计数器的值加一, 当计数器的值大于请求阈值时, 开始限流. 这个时间段结束后, 重新初始化计数器, 对新的时间窗口进行监控。
固定时间窗口存在临界突变的问题, 所谓临界突变, 是指在上一个时间段的最后时期和下一个时间段的开始时期突然涌入大量流量, 其结果是在各自时间段内流量控制产生效果, 但是在短时间内服务器会收到2倍于阈值数量的请求, 进而导致服务宕机, 如下图所示。
滑动时间窗口为固定窗口的改良版,解决了固定窗口在窗口切换时会受到两倍于阈值数量的请求,滑动窗口在固定窗口的基础上,将一个窗口分为若干个等份的小窗口,每个小窗口对应不同的时间点,拥有独立的计数器,当请求的时间点大于当前窗口的最大时间点时,则将窗口向前平移一个小窗口(将第一个小窗口的数据舍弃,第二个小窗口变成第一个小窗口,当前请求放在最后一个小窗口),整个窗口的所有请求数相加不能大于阀值。滑动时间窗口演示如下图:
3.1.1 基于Redis实现滑动时间窗口算法
Redis提供如下命令, 使用这几个命令可以方便快捷的实现滑动时间窗口算法进行限流
- ZADD key score member: 用于将一个或多个成员元素及其分数值加入到有序集当中
- ZREMRANGEBYSCORE key min max: 用于移除有序集中,指定分数(score)区间内的所有成员
- ZCARD key: 用于计算集合中元素的数量
lua代码如下:
local time = redis.call('TIME')
local cur_timestamp = time[1]*1000+time[2]/1000
local period = tonumber(ARGV[1])
local maxCount = tonumber(ARGV[2])
redis.call('ZADD', KEYS[1], cur_timestamp, cur_timestamp)
redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, cur_timestamp - period*1000)
redis.call('EXPIRE', KEYS[1], period)
local result
result = redis.call('ZCARD', KEYS[1])
if result > maxCount then
redis.call("ZREM", KEYS[1], cur_timestamp)
return 0
else
return 1
end
java调用lua脚本代码如下:
public boolean slide(String key, int period, int maxCount) {
final DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>();
defaultRedisScript.setResultType(Long.class);
defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/flowdemo/slide.lua")));
ImmutableList<String> keys = ImmutableList.of(key);
Object[] args = {period, maxCount};
long result = businessRedisClient.execute(defaultRedisScript, keys, args);
return result == 1;
}
3.2 漏桶算法
漏桶作为计量工具时,可以用于流量整形(Traffic Shaping)和流量控制(TrafficPolicing)。
漏桶算法的描述如下:
- 一个固定容量N的漏桶,按照常量固定速率V流出水滴;
- 如果桶是空的,则不需流出水滴;
- 可以以任意速率流入水滴到漏桶;
- 如果流入水滴超出了桶的容量,则流入的水滴溢出了(被丢弃),而漏桶容量是不变的。
漏桶算法示意图如下:
3.3 令牌桶算法
令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌。令牌桶算法的描述如下:
- 假设限制2r/s,则按照500毫秒的固定速率往桶中添加令牌;
- 桶中最多存放N个令牌,当桶满时,新添加的令牌被丢弃或拒绝;
- 当一个请求到达,将从桶中删除m个令牌,接着处理请求;
- 如果桶中的令牌不足m个,则不会删除令牌,且该请求将被限流(要么丢弃,要么缓冲区等待)。
令牌桶算法示意图如下:
3.4 基于Redis实现漏桶/令牌桶算法
漏桶/令牌桶算法是互逆的算法, 重点是控制速率, 漏桶是控制流出速率, 令牌桶是控制产生令牌的速率
Redis-Cell提供原子性的限流功能,并允许突发流量,可以很方便的应用于分布式环境中
下图是cli.throttle命令的解析图:
通过如下lua代码, 可以实现速率控制:
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local operations = tonumber(ARGV[2])
local period = tonumber(ARGV[3])
return redis.call('CL.THROTTLE',key,capacity,operations,period,1)
如果将速率用于控制流量的流出速度, 就可以实现漏桶算法:
如果将速率用于控制令牌的生成速度, 就可以实现令牌桶算法:
1: 是否成功,0:成功,1:拒绝
2: 令牌桶的容量,大小为初始值+1
3: 当前令牌桶中可用的令牌
4: 若请求被拒绝,这个值表示多久后才令牌桶中会重新添加令牌,单位:秒,可以作为重试时间
5: 表示多久后令牌桶中的令牌会存满
4 分布式流控组件: Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
Sentinel 的主要工作机制如下:
对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。
Sentinel提供了非常完善的官方文档,地址如下:
Sentinel官方wiki: https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5
4.1 Sentinel单机限流
Sentinel中主要概念是资源和规则,对于单机服务,只要设置了资源对应的限流规则,就可以对资源进行相应的保护
java代码实现如下:
public static void main(String[] args) {
// 配置规则
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
// 设置资源名称
rule.setResource("key");
// QPS 不得超出 1
rule.setCount(1);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default");
rules.add(rule);
// 加载规则
FlowRuleManager.loadRules(rules);
// 下面开始运行被限流作用域保护的代码
while (true) {
Entry entry = null;
try {
entry = SphU.entry("key");
System.out.println("hello world");
} catch (BlockException e) {
System.out.println("blocked");
} finally {
if (entry != null) {
entry.exit();
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
}
}
上诉代码运行结果如下:
4.2 Sentinel集群限流
集群限流需要一个配置中心,Sentinel官方提供了sentinel-dashboard项目作为集群限流的配置中心, 官方称其为Token-server。并且Sentinel还提供了多种客户端demo,只需要下载项目代码,即可体验Token-server完整演示。
项目代码下载地址为: git@github.com:alibaba/Sentinel.git
下图是Sentinel官方提供的demo:
Sentinel集群限流演示(客户端以spring-webmvc为例):
1.启动sentinel-dashboard项目
2.启动spring-webmvc项目(需在启动脚本添加参数:
-Dcsp.sentinel.dashboard.server=127.0.0.1:8080
-Dproject.name=sentinel-demo-spring-webmvc):
控制台应用列表展示:
控制台簇点链路页面展示:
控制台添加流控规则页面展示:
控制台试试监控页面展示:
作者:邓杰
链接:移动云开发者社区
来源:移动云官网开发者社区