前言在实际项目中,我在线上遇到过5W+QPS的峰值,也经历过压测下10W+QPS的大流量请求。关于并发流控制的一点思考。处理大流量的一些思路1、首先我们来说说什么是大流量?对于大流量,我们很可能会看到:TPS(每秒事务数)、QPS(每秒请求数)、1W+、5W+、10W+、100W+……其实没有绝对的数字。如果这个量对系统造成压力,影响系统的性能,那么这个量就可以称为大流量了。2.其次,应对大流量的常用手段有哪些?缓存:说白了就是让数据尽快进入缓存,靠近程序,不要频繁访问DB。降级:如果不是核心链接,则降级该服务。打个比方,今天的APP都是千人千面。拿到数据后,他们做个性化的排序展示。如果流量大,这个排序可以降级!地铁站会做一件事,就是限流!思路很直接,就是在一定时间内,将请求限制在一定范围内,保证系统不会被压垮,同时尽可能提高系统的吞吐量可能的。请注意,有时缓存和降级并不能解决问题。比如电商双十一期间,用户购买、下单等行为涉及到大量写操作,核心环节无法降级。这时候,限流就显得比较重要了。那么接下来,让我们重点关注限流。常见的限流方式常见的限流处理方式有:计数器、滑动窗口、漏桶、令牌。1、计数器计数器是一种比较简单的限流算法,用途广泛。在接口层面,很多地方都采用了这种方式来限流。在一段时间内,计数并与阈值比较,到达时间临界点时将计数器清零。代码示例:这里需要注意的是有一个临界时间的问题。比如12:01:00到12:01:58这段时间没有用户请求,然后在12:01:59的瞬间发送了100个请求,OK,然后在12的瞬间:02:00又提出了100个请求。这里大家应该能感觉到,在这个临界点,可能会容忍大量恶意用户的请求,甚至超出系统的预期容忍度。2.滑动窗口由于计数器的临界点缺陷,出现了滑动窗口算法来解决它。滑动窗口的意思是将固定的时间片进行划分,随着时间的推移而移动,巧妙的避免了计数器的临界点问题。也就是说,会统计固定的可以移动的格子个数来确定阈值,所以格子的多少影响了滑动窗口算法的精度。3、虽然滑动窗口有效的避免了时间临界点的问题,但是漏桶算法还是有时间片的概念,在这方面漏桶算法比滑动窗口更先进。有一个固定的水桶,取水的速度是不确定的,但出水的速度是恒定的,水满了就会溢出来。代码实现:4.令牌桶注意到漏桶的输出速度是恒定的,也就是说如果瞬时流量很大,大部分请求都会被丢弃(也就是所谓的溢出)。为了解决这个问题,对令牌桶的算法进行了改进。生成令牌的速率是恒定的,而请求令牌的速率是无限的。这意味着在面对瞬时的大流量时,算法可以在短时间内请求大量的token,获取token的过程并不是一件非常昂贵的事情。(就是生产代币和消费代币的意思)无论是拒绝取不到代币的代币桶,还是漏桶里的水溢出,都是为了保证大部分流量的正常使用而牺牲的。减少少量流量是合理的。如果需要保证少量流量,可能会导致系统达到限制挂掉。得不偿失。代码实现:限流神器:GuavaRateLimiterGuava不仅收集、缓存、异步回调等功能强大,还为我们封装了限流API!GuavaRateLimiter是基于令牌桶算法的,我们只需要告诉RateLimiter系统限制的QPS是多少,那么RateLimiter就会按照这个速度往桶中放入令牌,然后在请求的时候,通过RateLimiter获取权限(token)尝试获取()方法。代码示例:分布式场景限流上面提到的一些限流方法是针对单机的。其实在大多数场景下,单机限流就足够了。分布式下限流的方法往往需要多种技术的结合,比如Nginx+Lua,Redis+Lua等,本文主要讨论单机限流,分布式中的限流就不介绍了此处详细介绍场景。一句话,让系统的流量先在队列里排队,限流,不要让流量直接打到系统上。
