限流是什么?如何限流?怎么限流?-容器限流

有两种限流方式:

  1. tomcat限流:配置最大线程数
  2. nginx限流:一是控制速率,二是控制并发连接数

tomcat限流

spring项目直接可以在配置文件中设置

  • 使用 application.properties:src/main/resources/application.properties文件中添加以下配置
    server.tomcat.max-threads=200  # 设置最大线程数为 200
    
  • 使用 application.yml:在 src/main/resources/application.yml 中添加:
    server:
      tomcat:
        max-threads: 200  # 设置最大线程数为 200
    

nginx限流

控制速率:

  • 使用limit_req_zone用来限制单位时间内的请求数,即速率限制,
    • 示例配置如下:
    • 配置表示,限制每个 IP 访问的速度为 2r/s,因为 Nginx 的限流统计是基于毫秒的,我们设置的速度是 2r/s,转换一下就是 500ms 内单个 IP 只允许通过 1 个请求,从 501ms 开始才允许通过第 2 个请求
      limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
      server { 
          location / { 
              limit_req zone=mylimit;
          }
      }
      
  • burst 关键字:真实情况下我们应该控制一个 IP 单位总时间内的总访问次数,而不是像上面那么精确但毫秒,我们可以使用 burst 关键字开启此设置,它表示在限速时,允许的额外请求数量
  • 示例配置如下:
  • burst=4 表示每个 IP 最多允许4个突发请求
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
    server { 
        location / { 
            limit_req zone=mylimit burst=4;
        }
    }
    

控制并发数

  • 利用 limit_conn_zone 和 limit_conn 两个指令即可控制并发数
    • 示例配置如下:
    • 其中 limit_conn perip 10 表示限制单个 IP 同时最多能持有 10 个连接
      • 只有当 request header 被后端处理后,这个连接才进行计数。
    • limit_conn perserver 100 表示 server 同时能处理并发连接的总数为 100 个
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
    ...
    limit_conn perip 10;
    limit_conn perserver 100;
}

服务端限流算法

常见的限流算法有三种:

  • 计数器限流:主要用来限制总并发数,比如数据库连接池大小、线程池大小、接口访问并发数等都是使用计数器算法
  • 漏桶算法
    • 思路:漏桶算法思路很简单,我们把水比作是请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,漏桶里的水按一定速率流出,当流出的速率小于流入的速率时,由于漏桶容量有限,后续进入的水直接溢出(拒绝请求),以此实现限流
  • 令牌桶算法:可以理解成医院的挂号看病,只有拿到号以后才可以进行诊病
    • 系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。
    • 令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制
    • 漏桶算法区别
      • 漏桶的天然特性决定了它不会发生突发流量,就算每秒1000个请求到来,那么它对后台服务输出的访问速率永远恒定。
      • 令牌桶则不同,其特性可以“预存”一定量的令牌,因此在应对突发流量的时候可以在短时间消耗所有令牌,其突发流量处理效率会比漏桶高,导向后台系统的压力也会相应增多
  • 滑动窗口

计数器限流(固定窗口算法)

计数器限流算法也是比较常用的,主要用来限制总并发数,比如数据库连接池大小、线程池大小、程序访问并发数等都是使用计数器算法。也是最简单粗暴的算法。
常用的三个方法如下:

  1. 采用AtomicInteger使用AomicInteger来进行统计当前正在并发执行的次数,如果超过域值就简单粗暴的直接响应给用户,说明系统繁忙,请稍后再试或其它跟业务相关的信息。
    • 弊端:使用 AomicInteger 简单粗暴超过域值就拒绝请求,可能只是瞬时的请求量高,也会拒绝请求
  2. 采用令牌Semaphore:使用Semaphore信号量来控制并发执行的次数,如果超过域值信号量,则进入阻塞队列中排队等待获取信号量进行执行如果阻塞队列中排队的请求过多超出系统处理能力,则可以在拒绝请求
    • 相对Atomic优点:如果是瞬时的高并发,可以使请求在阻塞队列中排队,而不是马上拒绝请求,从而达到一个流量削峰的目的。
  3. 采用ThreadPoolExecutor java线程池固定线程池大小,超出固定先线程池和最大的线程数,拒绝线程请求

滑动窗口

滑动窗口算法是对固定窗口算法的改进

滑动窗口计数器(Sliding Window)算法限流:解决固定窗口临界值的问题。它将单位时间周期分为n个小周期,分别记录每个小周期内接口的访问次数,并且根据时间滑动删除过期的小周期

如下图

  • 每 500ms 滑动一次窗口,可以发现窗口滑动的间隔越短,时间窗口的临界突变问题发生的概率也就越小,
  • 不过只要有时间窗口的存在,还是有可能发生时间窗口的临界突变问题。
    sss

漏桶算法

漏桶的桶有大小,就如队列的容量,当请求堆积超过指定容量时,会触发拒绝策略。
漏桶模式中的消费处理总是能以恒定的速度进行,可以很好的保护自身系统不被突如其来的流量冲垮
在这里插入图片描述

令牌桶算法

最为常用的 Google 的 Java 开发工具包 Guava 中的限流工具类 RateLimiter 就是令牌桶的一个实现。令牌桶的实现思路类似于生产者和消费之间的关系
在这里插入图片描述

系统服务作为生产者,按照指定频率向桶(容器)中添加令牌,如 QPS 为 2,每 500ms 向桶中添加一个令牌,如果桶中令牌数量达到阈值,则不再添加

  • 1s / 阈值(QPS) = 令牌添加时间间隔。

请求执行作为消费者,每个请求都需要去桶中拿取一个令牌,取到令牌则继续执行;如果桶中无令牌可取,就触发拒绝策略,可以是超时等待,也可以是直接拒绝本次请求,由此达到限流目的

上一篇:【Golang】踩坑记录:make创建引用类型,初始值是不是nil!!