侧边栏壁纸
博主头像
再见理想博主等级

只争朝夕,不负韶华

  • 累计撰写 112 篇文章
  • 累计创建 64 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

限流解决方案

再见理想
2022-08-11 / 0 评论 / 0 点赞 / 391 阅读 / 2,302 字

根据限流作用范围,可以分为单机限流和分布式限流(Redis + Lua)
根据限流方式,又分为计数器、滑动窗口、漏桶和令牌桶限流;

一,限流算法

计数器

计数器是一种最简单限流算法,其原理就是:在一段时间间隔内,对请求进行计数,与阀值进行比较判断是否需要限流,一旦到了时间临界点,将计数器清零。
这种方法虽然简单,但也有个大问题就是没有很好的处理单位时间的边界。

滑动窗口

滑动窗口是针对计数器存在的临界点缺陷,所谓滑动窗口是一种流量控制技术,这个词出现在 TCP 协议中。滑动窗口把固定时间片进行划分,并且随着时间的流逝,进行移动,固定数量的可以移动的格子,进行计数并判断阀值。

其实计数器就是滑动窗口啊,只不过只有一个格子而已,所以想让限流做的更精确只需要划分更多的格子就可以了,为了更精确我们也不知道到底该设置多少个格子,格子的数量影响着滑动窗口算法的精度,依然有时间片的概念,无法根本解决临界点问题。

漏桶算法

漏桶算法(Leaky Bucket),原理就是一个固定容量的漏桶,按照固定速率流出水滴。
image.png

漏桶算法有以下特点:

  • 漏桶具有固定容量,出水速率是固定常量(流出请求)
  • 可以以任意速率流入水滴到漏桶(流入请求)
  • 如果流入水滴超出了桶的容量,则流入的水滴溢出(新请求被拒绝)

漏桶限制的是常量流出速率(即流出速率是一个固定常量值),所以最大的速率就是出水的速率,不能出现突发流量。

使用案例:Nginx 限流配置

令牌桶算法

系统以恒定速率向桶里放入令牌,如果桶满了则不加。当请求来的时候,从桶里拿走一个令牌,如果桶里没有令牌则阻塞或者拒绝新的请求。它允许突发的流量。
令牌桶的另外一个好处是可以方便的改变放入桶中的令牌的速率。
image.png

使用案例:java RateLimiter 工具

限流对比

计数器:

  • 优点:固定时间段计数,实现简单,适用不太精准的场景;
  • 缺点:对边界没有很好处理,导致限流不能精准控制。

滑动窗口:

  • 优点:将固定时间段分块,时间比“计数器”复杂,适用于稍微精准的场景;
  • 缺点:实现稍微复杂,还是不能彻底解决“计数器”存在的边界问题。

漏桶:

  • 优点:可以很好的控制消费频率;
  • 缺点:实现稍微复杂,单位时间内,不能多消费,感觉不太灵活。

令牌桶:

  • 优点:可以解决“漏桶”不能灵活消费的问题,又能避免过渡消费,强烈推荐;
  • 缺点:实现稍微复杂,其它缺点没有想到。

二,单机限流

guava

guava 是谷歌开源的 java 核心工具库,里面包括集合,缓存,并发等好用的工具,当然也提供了我们这里所需要的的限流的工具,核心类就是 RateLimiter,基于令牌桶算法实现。

//  RateLimiter rateLimiter = RateLimiter.create(100, 500, TimeUnit.MILLISECONDS); 预热的rateLimit
    RateLimiter rateLimiter = RateLimiter.create(100); // 简单的rateLimit
    boolean limitResult = rateLimiter.tryAcquire();

使用方式比较简单,如上面代码所示,我们只需要构建一个 RateLimiter,然后再调用 tryAcquire 方法,如果返回为 true 代表我们此时流量通过,相反则被限流。
优点:使用简单。

Sentinel

sentinel 是阿里巴巴开源的分布式服务框架的轻量级流量控制框架,承接了阿里巴巴近 10 年的双十一大促流量的核心场景,他的核心是流量控制但是不局限于流量控制,还支持熔断降级,监控等等。

  • 基于并发数限流: 设置 Grade 为 FLOW_GRADE_THREAD;
  • 基于QPS限流:
    • 默认策略: 设置 Behavior 为 CONTROL_BEHAVIOR_DEFAULT, 这个模式是滑动窗口计数器模式;
    • Warm Up:设置为 Behavior 为 CONTROL_BEHAVIOR_WARM_UP, 类似之前 guava 中介绍的 warmup。预热启动方式;
    • 匀速排队:设置 Behavior 为 CONTROL_BEHAVIOR_RATE_LIMITER, 这个模式其实就是漏桶算法;
    • Warm Up + 匀速排队:设置 Behavior 为 CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER,之前 warm up 到高水位之后使用的是滑动窗口的算法限流,这个模式下继续使用匀速排队的算法。
  • 基于调用关系限流: 可以基于调用关系去做更为灵活的限流;

缺点:

  • 使用较复杂;
  • sentinel 的使用往往需要搭建服务,对比 guava 的开箱即用来说,有一定的运维成本;
  • 限流统计有一定的并发问题;

三,分布式限流

Nginx 限流

Nginx 限流使用漏桶算法实现,Nginx 提供了两种限流手段:一是基于客户端ip进行速率控制,二是控制并发连接数。
文章:Nginx限流配置

Redis + Lua 分布式限流

单机版限流仅能保护自身节点,但无法保护应用依赖的各种服务,并且在进行节点扩容、缩容时也无法准确控制整个服务的请求限制。
而分布式限流,以集群为维度,可以方便的控制这个集群的请求限制,从而保护下游依赖的各种服务资源。
分布式限流最关键的是要将限流服务做成原子化 ,我们可以借助 Redis 的计数器,Lua 执行的原子性,进行分布式限流。
缺点:redis 的方案实现起来整体来说比较简单,但是强依赖我们的系统时间,如果不同机器之间的系统时间有偏差限流就有可能不准确。
文章:Redis + Lua 分布式限流

Sentinel

在 sentinel 中提供了集群的解决方案,这个对比其他的一些限流框架是比较有特色的。在 sentinel 中提供了两种模式:

  • 独立模式: 限流服务作为单独的 server 进行部署,如下图所示,所有的应用都向单独部署的 token-server 进行获取 token, 这种模式适用于跨服务之间的全局限流,比如下面图中,A 和 B 都会去 token-server 去拿,这个场景一般来说比较少,更多的还是服务内集群的限流比较多。
  • 内嵌模式:在内嵌模式下,我们会把 server 部署到我们应用实例中,我们也可以通过接口转换我们的 server-client 身份,当然我们可以自己引入一些 zk 的一些逻辑设置让我们的 leader 去当 server, 机器挂了也可以自动切换。这种比较适合同一个服务集群之间的限流,灵活性比较好,但是要注意的是大量的 token-server 的访问也有可能影响我们自己的机器。

四,限多少流

限多少流这个问题大部分的时候可能就是一个历史经验值,我们可以通过日常的 qps 监控图,然后再在这个接触上加一点冗余的 QPS 可能这个就是我们的限流了。
但是有一个场景需要注意,那就是大促 (这里指的是电商系统里面的场景,其他系统类比流量较高的场景) 的时候,我们系统的流量就会突增,再也不是我们日常的 QPS 了,这种情况下,往往需要我们在大促之前给我们系统进行全链路压测,压测出一个合理的上限,然后限流就基于这个上限去设置。

五,怎么去选择工具

如果没有集群限流或者熔断这些需求,我个人觉得选择 RateLimter 是一个比较不错的选择,应该其使用比较简单,基本没有学习成本,如果有其他的一些需求我个人觉得选择 sentinel。

0

评论区