当前位置: 首页 > 科技观察

Java架构师:高并发下的流量控制

时间:2023-03-12 18:09:02 科技观察

如果此时不采取保护措施,服务器会承受很大的处理压力,请求量大,服务器负载也大,当请求超过服务器负载限制,系统崩溃,导致所有人无法访问。对于应用服务的高可用,常用的方法是对大流量请求进行限流(秒杀/抢购),拦截大部分请求,只允许部分请求真正进入后端服务器,从而避免大量请求导致系统因压力过大而导致系统崩溃,从而保护服务的正常可用性。令牌桶、漏桶和计数器算法是最常用的三种限流算法。限流算法Counter计数器也是常用的限流算法,主要用来限制并发总数。比如限流qps为100,算法的思路是从第一个请求开始计时。在接下来的1s里,每有一个请求过来,计数就加1。如果累计数量达到100,那么后面的所有请求都会被拒绝。1s结束后,将计数恢复为0,重新开始计数。这种实现方式有一个弊端:如果我在单位时间的前10ms内已经通过了100个请求,那么在接下来的990ms里,我只能急切的拒绝请求。这种现象称为尖峰现象。为了消除尖峰现象,可以采用漏桶算法来实现限流。漏桶算法的名字很形象。算法内部有一个容器,类似于日常生活中使用的漏斗。一个请求进来,相当于往漏斗里倒水。然后从下端的小口慢慢均匀地流出。不管上面的水流有多大,下面的水流速度保持不变。不管服务调用者多么不稳定,通过漏桶算法限制流量,每10毫秒处理一个请求。因为处理速度是固定的,请求进来的速度是未知的。可能突然有很多请求进来,没有及时处理的请求会先放到桶里。既然是桶,那肯定是有上限的。如果桶已满,则丢弃新的传入请求。在算法实现上,可以准备一个队列来存放请求,使用线程池定时从队列中获取请求并执行,一次可以获取多个并发执行。该算法使用后也有缺点:无法应对短期的突发流量。同时它的优点是可以平滑网络上的突发流量,将请求整形为稳定的流量。令牌桶从某种意义上说,令牌桶算法是对漏桶算法的改进。桶算法可以限制请求调用的速率,令牌桶算法可以限制调用的平均速率,同时允许一定程度的突发调用。在令牌桶算法中,有一个桶用来存放固定数量的令牌。算法中有一种机制,以一定的速率将令牌放入桶中。每次请求调用都需要先获取token。只有拿到token,才能继续执行。否则,选择等待可用令牌,或者直接拒绝。放置代币的动作是连续进行的。如果桶中的令牌数量达到上限,令牌就会被丢弃,所以才会出现这样的情况。桶中总是有大量可用的令牌。这时候进来的请求可以直接获取token去执行,比如设置qps为100,那么限流器初始化后一秒,桶里已经有100个token了。此时服务还没有完全启动。限制器可以暂时承受100个请求。因此,只有当桶中没有令牌时,请求才会等待,最后相当于按一定的速率执行。实现思路:可以准备一个队列来存放令牌,通过线程池周期性的产生令牌放入队列中。每有一个请求过来,就从队列中获取一个token,继续往下执行。LeakyBucketVSTokenBucket:两者的主要区别是“漏桶算法”可以强制限制数据传输速率,而“令牌桶算法”可以限制平均数据传输速率,也允许一定程度的突发传播。在“令牌桶算法”中,只要令牌桶中有令牌,就允许突发传输数据,直到达到用户配置的阈值,因此适用于具有突发特性的流量。集群限流Redis请求窗口采用redis的定时计数方式。在指定的时间窗口周期内,允许通过的最大请求数。例如,为了限制一个资源被每个用户或商户访问的次数,5s内只能访问两次。或者一天只能调用1000次。这种需求是单机限流无法实现的。这种情况下,就需要通过集群限流来实现。如何实现?为了控制访问次数,肯定需要一个计数器,而这个计数器只能保存在第三方服务中,比如redis。大体思路:每次有相关操作,就向redis服务器发送一个incr命令。比如需要限制用户访问/index接口的次数,只需要将用户id和接口名拼接生成rediskey即可。用户每次访问这个界面时,只需要对这个key执行incr命令,给这个key加上过期时间,就可以实现指定时间的访问频率。NginxratelimitNginxratelimitmodulebyrequestrate采用漏桶算法,可以强制保证请求的实时处理速度不会超过设定的阈值。Nginx正式版有两个限制IP连接和并发的模块:-limit_req_zone用于限制单位时间内的请求数,即ratelimit,使用漏桶算法“leakybucket”。-limit_req_conn用于限制同时连接数,即并发限制。