本文转载自微信公众号《无敌码农》,作者:无敌码农。转载请联系无敌码农公众号。微服务架构中经常提到的一个概念就是“服务熔断限流”。之所以会如此频繁地提到这个概念,是因为在高并发场景下,瞬时流量峰值很容易超过微服务中各个系统的最大容量,导致服务整体不可用。因此,在设计高并发场景下的微服务架构时,需要根据服务所能承受的最大流量制定限流策略,以保证服务在高并发场景下的稳定性.今天这篇文章就和大家聊聊限流,包括常见的限流算法,以及目前微服务架构中主要的限流框架。限流的概念首先,什么是限流?其实日常生活中的限流场景随处可见。比如北京地铁,每天早高峰都是人山人海。如果大家一起冲进车站,很容易造成车站拥堵。因此,地铁站一般都会在入口处设置迷宫式的围栏,大家分批绕圈进站。这是一个典型的限流场景。那么微服务中的限流到底是什么意思呢?从字面上看,限流是指流量,不同场景对流量的定义不同,可以是QPS(每秒请求数)、TPS(每秒交易数),也可以指纯网络流量(比如网卡通过的字节数)等。但是我们通常所说的限流是指限制到达系统的并发请求数,使得系统在自身能力允许的情况下能够正常处理一些用户请求,并拒绝超出自身处理能力的用户请求,从而保证系统的稳定运行。限流必然会导致用户请求失败或变慢,一定程度上会影响用户体验。因此,限流策略的制定需要根据系统压测结果,兼顾用户体验和系统稳定性。之间的平衡。限流的必要性?前面我们提到,限流的主要目的是为了保证系统的稳定性。在日常业务中,如果遇到双十一等促销活动,或者遇到爬虫等异常流量,用户流量会突然增加,但后台服务的处理能力有限,如果不能有效应对突发流量,后端服务很容易不堪重负。你可以想象这样一个场景:“一个服务的单个节点可以承受的QPS是1000,这个服务有5个节点,日常情况下这个服务的QPS是3000”。一般情况下,服务是没有压力的。按照负载均衡配置3000/5=600,每个节点每天的QPS只有600左右。直到有一天,老板突然搞了一波促销活动,系统整体QPS达到了8000。此时每个节点的平均负载QPS为1600,节点A最先失败,直接挂掉。此时集群中还有4个节点,每个节点的平均负载QPS会达到2000。因此,剩下的4个节点也接连挂掉,整个服务就崩溃了。而如果我们的系统有限流机制,情况会如何发展?系统整体QPS达到8000,但由于集群整体流量限制为5000,超过集群容量的3000个请求会被拒绝,系统会正常处理5000个用户请求,这就是限制集群整体流量的情况。对于每个节点,由于我的容忍度只有1000QPS,超过1000的部分也会被拒绝。这样虽然丢失了部分用户请求,但是保证了整个系统的稳定性,同时也为系统扩展留出了开发和维护的时间。由此可见,限流对于系统的自我保护非常重要。那么如何做限流呢?接下来,我们总结一下常见的限流算法。常见的限流算法常见的限流算法主要有:计数器、固定窗口、滑动窗口、漏桶、令牌桶。下面分别介绍这几种限流算法。<计数器限流>计数器限流是最简单粗暴的限流算法。例如,如果系统可以同时处理100个请求,那么你可以保存一个计数器,处理一个请求,将计数器加一,处理完一个请求后将计数器减一。一。每次有请求进来,看一下计数器的值,超过阈值就直接拒绝。具体实现中,如果单机内存中存在该计数器,则实现单机限流;如果存在,比如在Redis中,集群中的所有节点轮流做限流依据,那么就实现了集群限流算法。优点:实现简单,单机可以实现Java的Atomic等原子类,通过Redis的incr操作可以快速实现集群。缺点:逆流限流无法应对突发的流量增长。比如我们允许的阈值是1W,此时计数器的值为0,那么当所有1W的请求瞬间进来的时候,服务可能就承受不住了。这是因为系统上的压力在流量缓慢增加和突然流入之间是不同的。而且一般的限流是限制在指定时间间隔内的访问次数,而不是全时服务的整体处理能力,所以计数器限流不适合在高并发场景下实现限流。<固定窗口限流>与计数器相比,固定窗口限流是基于一定时间内的流量,每经过一个时间窗口,计数器自动复位。规则如下:如果请求数小于阈值,允许访问,计数器加1;如果请求数大于阈值,则拒绝访问;时间窗过去后,计数器自动清零;虽然固定窗口限流看起来很完美,但固定窗口临界性存在问题。例如,系统允许每秒1000个请求。如果第一个时间窗口的间隔是0到1秒,但是在0.55秒突然有1000个请求涌入,1秒后计数清零,此时1.05秒又有1000个请求涌入。此时,虽然固定时间窗口内的计数没有超过阈值,但是从全局来看,从0.55秒到1.05秒,2000个请求在0.5秒内涌入,这对于一个阈值为1000/s的系统来说是无法承受的.如下图所示:为了解决这个问题,推导了滑动窗口限流的算法!<滑动窗口限流>滑动窗口限流解决了固定窗口临界值的问题,可以保证没有时间窗口超过限流阈值。与固定窗口相比,滑动窗口除了需要引入计数器外,还需要在时间窗口中额外记录每个请求的到达时间。以1秒的时间窗口为例,规则如下:记录每次请求的时间;统计每个请求向前推送1秒的时间窗口内的请求数,可以删除1秒之前的数据;对请求进行计数,如果小于阈值,则记录请求的时间并允许通过,否则拒绝请求;虽然看起来还可以,但是滑动窗口并不能在短时间内解决集中流量的影响。例如,每秒有1000个请求的限制,但阈值可能会在前5毫秒内被完全填满。理想情况下,每10毫秒发出100个请求,这样系统会更顺畅地处理流量。但是在实际场景中很难控制请求的频率。因此,为了解决时间窗算法的痛点,漏桶算法出现了。<漏桶限流>漏桶算法的基本思想是流量不断进入漏桶,底层以固定速率处理请求。如果流量进入的速率高于底层处理请求的速率,桶中的流量超过的大小时,流量就会溢出。具体如下图所示:漏桶算法的特点是宽流入严流出。不管请求率多高,底层的处理速度都是匀速进行的。这种算法的特点有点类似于消息队列的处理机制。一般来说,漏桶算法也是由队列来实现的。但是漏桶算法的这个特点其实就是它的优点和缺点。有时候面对突如其来的流量,我们往往希望在保持系统稳定的同时,能够更快的处理用户请求,提升用户体验,而不是一步步佛工。在这种情况下,出现了令牌桶等限流算法,在处理突发流量时可以比漏桶算法更具攻击性。<令牌桶限流>令牌桶的原理和漏桶类似,只是漏桶是在底部匀速处理,而令牌桶是匀速往桶里塞令牌,然后只有拿到token后才能处理请求。将由服务器处理。具体规则如下:以固定的比例将令牌放入桶中;如果令牌数量超过桶的限制,它们将被丢弃;当有请求到来时,首先向桶中索取token,如果请求成功则处理,否则拒绝;可以看出,在处理突发流量时,令牌桶并没有像漏桶一样进行匀速处理,而是请求可以在短时间内同时取出桶中的令牌,并在服务器端处理及时。所以在应对突发流量的场景下,令牌桶的表现更好。限流算法总结经过上面的描述,好像漏桶和令牌桶比时间窗算法好很多,那么时间窗算法就没用了吗?其实不是的,虽然leakybuckets和tokenbuckets相比timewindow算法在流量整形上效果更好,但是它们也有自己的缺点,比如tokenbuckets。如果上线时系统没有预热,可能会因为此时桶中没有令牌而导致请求被阻塞。在过失杀人的情况下;在leakybucket中,由于请求是临时存储在bucket中的,所以请求能够被处理时存在延迟,不符合互联网服务的低延迟要求。因此,令牌桶和漏桶算法更适合阻塞限流场景,即后台任务限流。基于时间窗的限流更适用于互联网实现业务限流的场景,即可以处理快速处理,但不能及时响应调用者,避免等待时间过长要求。微服务限流组件如果有兴趣其实可以自己实现一个限流组件,不过这种轮子已经有人造了。目前市面上比较流行的限流组件主要有:谷歌Guava提供的限流工具“RateLimiter”,以及阿里开源的Sentinel。其中GoogleGuava提供的限流工具类“RateLimiter”是基于令牌桶实现的,并扩展了算法支持warm-up功能。阿里的Sentinel中统一速率限流策略采用的是漏桶算法。原文链接:https://mp.weixin.qq.com/s/A_iCvZKr1Gq2hUfV8PJ-RA
