在开发具有较高并发访问的系统时,大家知道的有哪些手段可以保护后端应用不被请求击垮?比如我们可以分析出有哪些服务的访问量较大,采用缓冲的方式提供数据。另外,目前分布式服务框架,例如Dubbo、以及Spring Cloud都提供了服务的治理功能,采用服务降级策略,可以提供Mock值的方式保护服务。当然,上面的办法明显时牺牲了用户体验,保全应用的做法,对有些场景的服务,使用这种做法是可行的。但是对于某些对服务资源要求非常严格的系统中,例如抢票、秒杀、下单购买等场景下,需要给用户明确的反馈才是最重要的,要么成功,要么失败。
今天我们主要介绍下限流算法:
令牌桶
漏桶
滑动窗口
计数器
令牌桶
令牌桶算法有一个容量固定的令牌桶,该桶可以按照固定的速度生产Token。根据限流的方式不同,消耗令牌的方式也不同。
例1:我们对API进行限流,防止爬虫或者异常刷接口,我们限的是对API的调用,因此一次API调用,消耗一个token。
例2:我们还可以根据请求的数据包进行限流,通过保护服务的带宽保护服务的安全。我们可以认为一个字节的请求数据包对应一个token。
令牌桶
令牌桶算法具有如下特性:
算法可以设置生产速率,例如2r/s,这样500ms就会有一个token产生。
桶具有上限,满了之后,新生成的token被丢弃。
消耗token时,token不足无法消耗。
容易有洪峰(令牌满的时候)。
代码示例:
RateLimiter limiter = RateLimiter.create(10.0); // 每秒不超过10个任务被提交
for (int i = 0; i < 10; i++) {
limiter.acquire(); // 请求RateLimiter, 超过permits会被阻塞
System.out.println("call execute.." + i);
}
漏桶
漏桶和沙漏似的,一个计量工具,具有固定的速率控制。
漏桶具有固定流速。
可空,不流出。
漏桶进入速度随意。
容量固定,满则溢。
令牌桶是按照固定速率向桶里面添加令牌,请求是否被处理要看桶中是否有足够的令牌。流通是按照固定的流速漏出请求流量。令牌桶允许突发请求,漏桶则平滑请求。
漏桶
滑动窗口
在TCP通信中,使用了滑动窗口做拥塞控制。需要双方参与:发送方和接收方。简单来说,就是接收方要告诉发送方,你不要发的太快,多了我接收不了。发送方为了把数据安全送达,当然要听接收方的建议。这种限流需要有对应的算法支持。
计数器
有时候我们可以使用简单的计数器实现限流。我们可以限制一定时间内的访问并发数,这样简单粗暴。例如我们设定一定时间内的连接数不能高于100,数据库连接数、线程创建数等。这种方式不够平滑,但可以应对简单的服务限流场景,实际上我们可以结合滑动时间主动失效并转移计数的起点,用可以实现。
总结:
限流只是保护系统安全的方式一种手段而已,可以嵌套在应用中,也可以在代理上做入口限流。nginx使用Lua脚本做。后续慢慢说明,这些比较简单。实际上我们在应用中,用的比较多的还是令牌桶和漏桶两种算法,在Google的Guava库中有对应的实现。
