限流的几种方案

科技资讯 投稿 6000 0 评论

限流的几种方案

文章目录

    • QPS和连接数控制
    • 传输速率
    • 黑白名单
    • 分布式环境
  • 限流方案常用算法

    • 令牌桶算法
    • 漏桶算法
    • 滑动窗口
    • Nginx限流
    • 中间件限流
    • 限流组件
    • 合法性验证限流
    • Guava限流
    • 网关层限流
  • 从架构维度考虑限流设计

    • Tomcat限流

限流基本概念

对一般的限流场景来说它具有两个维度的信息:

    时间 限流基于某段时间范围或者某个时间点,也就是我们常说的“时间窗口”,比如对每分钟、每秒钟的时间窗口做限定
  • 资源 基于可用资源的限制,比如设定最大访问次数,或最高可用连接数

QPS和连接数控制

对于连接数和QPS限流来说,我们可设定IP维度的限流,也可以设置基于单个服务器的限流。

最全面的Java面试网站

传输速率

黑白名单

黑白名单是各个大型企业应用里很常见的限流和放行手段,而且黑白名单往往是动态变化的。举个例子,如果某个IP在一段时间的访问次数过于频繁,被系统识别为机器人用户或流量攻击,那么这个IP就会被加入到黑名单,从而限制其对系统资源的访问,这就是我们俗称的“封IP”。

有时我们还会发现公司的网络无法访问12306这类大型公共网站,这也是因为某些公司的出网IP是同一个地址,因此在访问量过高的情况下,这个IP地址就被对方系统识别,进而被添加到了黑名单。使用家庭宽带的同学们应该知道,大部分网络运营商都会将用户分配到不同出网IP段,或者时不时动态更换用户的IP地址。

分布式环境

分布式区别于单机限流的场景,它把整个分布式环境中所有服务器当做一个整体来考量。比如说针对IP的限流,我们限制了1个IP每秒最多10个访问,不管来自这个IP的请求落在了哪台机器上,只要是访问了集群中的服务节点,那么都会受到限流规则的制约。

    网关层限流 将限流规则应用在所有流量的入口处
  • 中间件限流 将限流信息存储在分布式环境中某个中间件里(比如Redis缓存),每个组件都可以从这里获取到当前时刻的流量统计,从而决定是拒绝服务还是放行流量
  • sentinel,springcloud生态圈为微服务量身打造的一款用于分布式限流、熔断降级等组件

限流方案常用算法

令牌桶算法

Token Bucket令牌桶算法是目前应用最为广泛的限流算法,顾名思义,它有以下两个关键角色:

    令牌 获取到令牌的Request才会被处理,其他Requests要么排队要么被直接丢弃
  • 桶 用来装令牌的地方,所有Request都从这个桶里面获取令牌 主要涉及到2个过程:
  • 令牌生成

对于令牌生成器来说,它会根据一个预定的速率向桶中添加令牌,比如我们可以配置让它以每秒100个请求的速率发放令牌,或者每分钟50个。注意这里的发放速度是匀速,也就是说这50个令牌并非是在每个时间窗口刚开始的时候一次性发放,而是会在这个时间窗口内匀速发放。

本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~

如果访问不了Github,可以访问gitee地址。

    令牌获取

每个访问请求到来后,必须获取到一个令牌才能执行后面的逻辑。假如令牌的数量少,而访问请求较多的情况下,一部分请求自然无法获取到令牌,那么这个时候我们可以设置一个“缓冲队列”来暂存这些多余的令牌。

当队列已满的情况下,这部分访问请求将被丢弃。在实际应用中我们还可以给这个队列加一系列的特效,比如设置队列中请求的存活时间,或者将队列改造为PriorityQueue,根据某种优先级排序,而不是先进先出。

漏桶算法

漏桶算法的前半段和令牌桶类似,但是操作的对象不同,令牌桶是将令牌放入桶里,而漏桶是将访问请求的数据包放到桶里。同样的是,如果桶满了,那么后面新来的数据包将被丢弃。

    漏桶 vs 令牌桶的区别

根据它们各自的特点不难看出来,这两种算法都有一个“恒定”的速率和“不定”的速率。令牌桶是以恒定速率创建令牌,但是访问请求获取令牌的速率“不定”,反正有多少令牌发多少,令牌没了就干等。而漏桶是以“恒定”的速率处理请求,但是这些请求流入桶的速率是“不定”的。

滑动窗口

比如说,我们在每一秒内有5个用户访问,第5秒内有10个用户访问,那么在0到5秒这个时间窗口内访问量就是15。如果我们的接口设置了时间窗口内访问上限是20,那么当时间到第六秒的时候,这个时间窗口内的计数总和就变成了10,因为1秒的格子已经退出了时间窗口,因此在第六秒内可以接收的访问量就是20-10=10个。

常用的限流方案

合法性验证限流

比如验证码、IP 黑名单等,这些手段可以有效的防止恶意攻击和爬虫采集;

Guawa限流

RateLimiter为首的几个限流支持类,但是作用范围仅限于“当前”这台服务器,也就是说Guawa的限流是单机的限流,跨了机器或者jvm进程就无能为力了 比如说,目前我有2台服务器[Server 1Server 2],这两台服务器都部署了一个登陆服务,假如我希望对这两台机器的流量进行控制,比如将两台机器的访问量总和控制在每秒20以内,如果用Guava来做,只能独立控制每台机器的访问量<=10。

网关层限流

服务网关,作为整个分布式链路中的第一道关卡,承接了所有用户来访请求,因此在网关层面进行限流是一个很好的切入点 上到下的路径依次是:

    用户流量从网关层转发到后台服务
  1. 后台服务承接流量,调用缓存获取数据
  2. 缓存中无数据,则访问数据库

然后经过后台服务的验证逻辑之后,刷掉了一部分错误请求,剩下的请求落在缓存上,如果缓存中没有数据才会请求漏斗最下方的数据库,因此数据库层面请求数量最小(相比较其他组件来说数据库往往是并发量能力最差的一环,阿里系的MySQL即便经过了大量改造,单机并发量也无法和Redis、Kafka之类的组件相比)

Nginx限流

Nginx 提供了两种限流方法:一种是控制速率,另一种是控制并发连接数。

控制速率

limit_req_zone 用来限制单位时间内的请求数,即速率限制,

    控制速率优化版

上面的速率控制虽然很精准但是在生产环境未免太苛刻了,实际情况下我们应该控制一个IP单位总时间内的总访问次数,而不是像上面那样精确到毫秒,我们可以使用 burst 关键字开启此设置

burst=4意思是每个IP最多允许4个突发请求

控制并发数

limit_conn_zone 和 limit_conn 两个指令即可控制并发数

limit_conn perip 10 表示限制单个 IP 同时最多能持有 10 个连接;limit_conn perserver 100 表示 server 同时能处理并发连接的总数为 100 个。

中间件限流

那么想必大家都能想到,必然是redis了,利用Redis过期时间特性,我们可以轻松设置限流的时间跨度(比如每秒10个请求,或者每10秒10个请求)。同时Redis还有一个特殊技能–脚本编程,我们可以将限流逻辑编写成一段脚本植入到Redis中,这样就将限流的重任从服务层完全剥离出来,同时Redis强大的并发量特性以及高可用集群架构也可以很好的支持庞大集群的限流访问。【reids + lua】

限流组件

从架构维度考虑限流设计

在真实的项目里,不会只使用一种限流手段,往往是几种方式互相搭配使用,让限流策略有一种层次感,达到资源的最大使用率。在这个过程中,限流策略的设计也可以参考前面提到的漏斗模型,上宽下紧,漏斗不同部位的限流方案设计要尽量关注当前组件的高可用。

具体的实现限流的手段

1)Tomcat 使用 maxThreads来实现限流。

limit_req_zone和 burst来实现速率限流。

limit_conn_zone和 limit_conn两个指令控制并发连接的总数。

5)漏桶算法可以使用Redis-Cell来实现。

需要注意的是借助Redis实现的限流方案可用于分布式系统,而guava实现的限流只能应用于单机环境。如果你觉得服务器端限流麻烦,可以在不改任何代码的情况下直接使用容器限流(Nginx或Tomcat),但前提是能满足项目中的业务需求。

Tomcat限流

conf/server.xml 配置中,maxThreads 就是 Tomcat 的最大线程数,当请求的并发大于此值(maxThreads)时,请求就会排队执行,这样就完成了限流的目的。

maxThreads 的值可以适当的调大一些,Tomcat默认为 150(Tomcat 版本 8.5),但这个值也不是越大越好,要看具体的服务器配置,需要注意的是每开启一个线程需要耗用 1MB 的 JVM 内存空间用于作为线程栈之用,并且线程越多 GC 的负担也越重。

最后给大家分享一个Github仓库,上面有大彬整理的300多本经典的计算机书籍PDF,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~

Github地址

编程笔记 » 限流的几种方案

赞同 (28) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽