文件下载限流设计
上网查找学习资料的时候,经常遇到别人分享百度网盘资源,但是网盘下载速度实在是感人,亦或者碰到每天限制下载次数,想要多下载,不好意思请充值vip。
本文思维导图
业务设计背景
- 防止过多的请求导致资源耗尽或服务崩溃
- 倒逼用户充值vip
限流相关概念:
- QPS:指每秒钟处理的请求或查询次数,是衡量系统性能的重要指标。
- 连接数:表示系统当前同时存在的连接数量,其中连接可以是网络连接、数据库连接等。
QPS和连接数的关系
直观关系: 通常情况下,更高的QPS可能需要更多的连接数,因为每个请求都需要建立一个连接。特别是在Web应用中,每个用户请求通常对应一个连接。
不是线性关系: 增加连接数不一定会线性增加QPS。在某些情况下,可能会出现瓶颈,例如数据库连接池、网络带宽等,导致QPS增长放缓。
受限于系统资源: 增加连接数会增加系统资源的消耗,包括内存、CPU、文件描述符等。系统的硬件和软件架构可能对连接数和QPS有一定的限制。
QPS的计算: QPS通常是通过对请求总数除以时间来计算的。例如,如果系统在一秒钟内处理了1000个请求,那么QPS就是1000。
- 传输速率:指在网络或通信系统中,数据传输的速度或速率。它通常以比特每秒(bps,bits per second)为单位表示。
- 黑白名单:一种常见的访问控制机制,用于限制或允许对系统、网络、应用程序等资源的访问。
- 角色权限:一种在信息系统中实现访问控制的关键机制。
限流的原理
限流的原理是通过控制请求的数量或速度,使之不超过预设的阈值,从而避免服务器过载或拒绝服务。
限流层面
- 网络层:涉及对网络连接的控制,例如通过设置最大连接数、使用网络设备进行流量控制等。
- 应用层:对应用的请求或操作进行控制。这可能包括对HTTP请求的限制、对API调用的频率控制等。
- 业务层:具体地应用于业务逻辑。根据角色设置相应限流策略。
限流算法
- 滑动窗口算法:
滑动窗口算法是一种基于时间窗口的限流方法。
时间被划分为固定大小的窗口,每个窗口内允许通过的请求数量受到限制。
通过记录每个时间窗口内的请求次数,系统可以根据预设的阈值来判断是否允许新的请求通过。
优势:简单直观: 实现相对简单,易于理解。适用于平滑流量: 在需要平滑限制流量的场景下表现良好。
劣势:不适应突发流量: 对于突发流量的控制能力较弱。
- 令牌桶算法:
令牌桶算法是一种基于令牌的限流方法。
系统以恒定的速率往令牌桶中添加令牌,每个令牌代表一个允许通过的请求。
当请求到达时,需要从令牌桶中获取令牌,如果桶中有足够的令牌,则允许通过;否则,拒绝请求。 令牌桶算法可以应对突发流量,保持相对平滑的请求速率。
优势:突发流量控制: 对于突发流量有较好的控制能力。平滑请求速率: 通过令牌桶的机制,可以实现相对平滑的请求速率。
劣势:复杂性稍高: 相对于滑动窗口算法,实现相对复杂一些。
- 漏桶算法:
漏桶算法是一种基于漏桶的限流方法。
想象一个水桶,水以固定速率流出,如果桶满了,多余的水会被溢出丢弃。
在限流中,请求被看作是水,漏桶代表了固定的处理能力。当请求到达时,如果漏桶有足够容量,允许通过;否则,拒绝请求。
漏桶算法可以平滑突发的请求流量,对于限制请求速率很有用。
优势:平滑流量输出: 提供平滑的流量输出,有助于维护系统的稳定性。简单可控: 实现相对简单,容易控制。 劣势:不适应突发流量: 类似于滑动窗口算法,对于突发流量的控制相对较弱。
总结:令牌桶算法在综合性能上较为平衡,常被广泛应用。滑动窗口算法适合对平滑流量有要求的场景,漏桶算法适用于对平滑流量输出有更强要求的场景。
常用限流方案
- 合法性验证限流: 在进行请求限流的同时,还要对请求的合法性进行验证。
一般实践
用户身份验证:确保用户是合法的、已登录的用户,而不是未经验证的匿名请求。
权限验证:针对不同的操作或资源,进行权限验证。
请求参数验证:对请求中的参数进行验证,确保其合法性和完整性。
防御机制:集成防御机制,例如防止 CSRF(跨站请求伪造)攻击、防止 SQL 注入等.
设备识别:针对请求的设备信息进行识别,确保请求来自合法的设备。
行为分析:分析用户的行为模式,检测异常行为。
日志记录和监控:记录请求日志,包括用户身份、请求内容等信息。
Guava 限流:通过 RateLimiter 类来实现。Guava 的限流工具简单易用,适用于一些简单的场景,对于复杂的分布式系统,可能需要使用更强大的分布式限流工具,如 Redis、Zookeeper 等。
网关层限流
Tomcat限流:
修改maxConnections(最大连接数)、maxThreads(最大线程数)、acceptCount(最大等待数)
参考链接:秒懂:tomcat的maxConnections、maxThreads、acceptCount 图解 - 疯狂创客圈 - 博客园
Nginx限流:
配置limit_req、burst等参数
中间件限流:根据消息中间件的自带特性,通过配置消息队列的参数,如最大消息数、消息过期时间等来控制下载。并且消息中间件能够存储下载任务,当下载服务异常的情况下,可以继续后续的下载。
限流组件:引入一些成熟框架的限流组件。例如,如果使用Java,你可以考虑使用Guava RateLimiter、Resilience4j等库,或者Spring Cloud Gateway等组件。配置限流组件以定义下载接口的限流规则。这可能包括每秒允许的请求数、每个请求的带宽等参数,具体取决于业务需求。
限流下载设计考虑方向
为了实现一个限流下载功能,我们需要考虑以下几个方面:
- 如何识别用户:我们需要有一种机制来区分不同的用户,例如使用IP地址、Cookie、Token等。这样我们才能对每个用户进行单独的限流策略。
- 如何限制下载速度:我们需要有一种机制来控制用户下载文件的速度,例如使用HTTP协议中的Range头来分段传输文件,并且在每个分段之间设置延迟或睡眠时间。这样我们就可以模拟出一个固定或变化的下载速度。
- 如何限制下载次数:我们需要有一种机制来记录用户下载文件的次数,在一定时间窗口内,如果用户下载次数超过阈值,则拒绝后续的下载请求。这样我们就可以防止用户重复或恶意下载文件。
具体案例
某个系统需要设计一个下载功能,但是这个下载功能对系统压力比较大,需要增加一定的限制,比如系统每秒处理5个下载请求,每个用户每日下载5次,后续请求按照先来后到排队等待执行,怎么设计?
业务分析:
- 已经有下载功能
- 用户层面限制每日下载次数
- 应用层面限制每秒下载次数以及请求排队等待执行
技术分析: 应用层面:使用令牌桶算法实现一个限流器(或者单机的话使用Guava 的限流工具也够用了)
用户层面限制,通过配置实现用户层面下载限制
下载高峰期:
简单实现
- 在下载的核心方法上增加限流器,超出限制返回提示
缺点:不够友好,需要用户等待重新操作 - 通过内部线程池执行业务,对核心下载方法增加内部限流,超过限制后线程池排队执行,前端轮询下载结果
缺点:应用意外宕机时,线程池待执行业务会丢失,导致下载失败
- 在下载的核心方法上增加限流器,超出限制返回提示
复杂实现 3. 使用redis数据结构实现队列,下载方法使用内部线程池执行业务。
优点:可以保存下载任务,应用异常数据也不会丢失缺点:需要额外编写队列执行程序,增加了程序复杂度 4. 同3,将Redis换成数据库,实现持久化下载请求,优缺点同3 5. 使用消息队列,利用MQ本身的特性队列和消息推送实现,相比前面方案编码更加简单,同时也能够实现下载功能异常的恢复后能够继续执行下载任务总结: 上述5个方案实现最简单的是1,但是有丢失任务的风险,最全面的是5
代码执行流程图
