跳至主要內容

文件下载限流设计

Neo大约 10 分钟编程业务

上网查找学习资料的时候,经常遇到别人分享百度网盘资源,但是网盘下载速度实在是感人,亦或者碰到每天限制下载次数,想要多下载,不好意思请充值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 等。

  • 网关层限流

限流下载设计考虑方向

为了实现一个限流下载功能,我们需要考虑以下几个方面:

  • 如何识别用户:我们需要有一种机制来区分不同的用户,例如使用IP地址、Cookie、Token等。这样我们才能对每个用户进行单独的限流策略。
  • 如何限制下载速度:我们需要有一种机制来控制用户下载文件的速度,例如使用HTTP协议中的Range头来分段传输文件,并且在每个分段之间设置延迟或睡眠时间。这样我们就可以模拟出一个固定或变化的下载速度。
  • 如何限制下载次数:我们需要有一种机制来记录用户下载文件的次数,在一定时间窗口内,如果用户下载次数超过阈值,则拒绝后续的下载请求。这样我们就可以防止用户重复或恶意下载文件。

具体案例

某个系统需要设计一个下载功能,但是这个下载功能对系统压力比较大,需要增加一定的限制,比如系统每秒处理5个下载请求,每个用户每日下载5次,后续请求按照先来后到排队等待执行,怎么设计?

业务分析

  • 已经有下载功能
  • 用户层面限制每日下载次数
  • 应用层面限制每秒下载次数以及请求排队等待执行

技术分析: 应用层面:使用令牌桶算法实现一个限流器(或者单机的话使用Guava 的限流工具也够用了)

用户层面限制,通过配置实现用户层面下载限制

下载高峰期:

  • 简单实现

    1. 在下载的核心方法上增加限流器,超出限制返回提示 缺点: 不够友好,需要用户等待重新操作
    2. 通过内部线程池执行业务,对核心下载方法增加内部限流,超过限制后线程池排队执行,前端轮询下载结果 缺点: 应用意外宕机时,线程池待执行业务会丢失,导致下载失败
  • 复杂实现 3. 使用redis数据结构实现队列,下载方法使用内部线程池执行业务。 优点: 可以保存下载任务,应用异常数据也不会丢失 缺点: 需要额外编写队列执行程序,增加了程序复杂度 4. 同3,将Redis换成数据库,实现持久化下载请求,优缺点同3 5. 使用消息队列,利用MQ本身的特性队列和消息推送实现,相比前面方案编码更加简单,同时也能够实现下载功能异常的恢复后能够继续执行下载任务

    总结: 上述5个方案实现最简单的是1,但是有丢失任务的风险,最全面的是5

    代码执行流程图