Kotlin中的速率限制与节流:如何保护API免受过载

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

当你的API发出"SOS"信号:认识速率限制与节流

\n\n

想象一下:你编写了一个完美的Kotlin应用程序。它快速、优雅,使用了协程和Flow。你将其部署到生产环境,然后——砰!——一个"好心"的用户(或者更糟,一个恶意攻击者)开始每秒向你的端点发送数千个请求。服务器崩溃,数据库哭泣,其他用户只能收到503 Service Unavailable。

\n\n

熟悉这种痛苦吗?正是为了应对这种情况,才有了速率限制和节流机制。在本文中,我们不仅会讨论理论,还会通过实际示例,讲解如何在Kotlin中实现这些模式。

\n\n

我们将编写真正能保护你后端的代码,无论是基于Ktor、Spring Boot的应用,还是纯协程项目。开始吧!

\n\n

速率限制 vs 节流:有什么区别(为什么这很重要)?

\n\n

我们从基础开始,以免混淆术语。虽然这两个概念经常被当作同义词使用,但它们之间是有细微差别的。

\n\n
    \n
  • 速率限制——是一种硬性限制。"你每分钟只能发送100个请求。第101个请求之后,你将收到429 Too Many Requests,并被限制到下一分钟"。这是关于限制。
  • \n
  • 节流——是一种更温和的调节。"你不能以超过每200毫秒一次的频率发送请求。如果发送太快,我们会将其放入队列或直接忽略"。这是关于速度。
  • \n
\n\n

在Kotlin世界中,我们最常在API层面(中间件)实现速率限制,在具体操作层面(例如,避免向外部服务器发送过多请求)实现节流。

\n\n

每个Kotlin开发者都应了解的算法

\n\n

在编写代码之前,让我们先选择工具。有三种经典算法非常适合Kotlin架构。

\n\n

1. 令牌桶——最流行的算法

\n

想象一个桶,每秒有N个新令牌滴入其中。桶有一个最大容量(突发容量)。要处理一个请求,需要取出一个令牌。如果没有令牌,请求将被拒绝。

\n

优点:允许小的突发负载,实现简单。

\n\n

2. 漏桶——平滑峰值

\n

一个有漏洞的桶。请求进入桶中,而"漏洞"以恒定速率释放它们。如果桶满了,请求将被丢弃。

\n

优点:完美地均衡负载,不会产生峰值。

\n\n

3. 滑动窗口——无抖动的精确性

\n

与固定的时间边界(如"1分钟")不同,我们观察一个滑动的时间段。实现起来更复杂,但能更准确地反映实际负载。

\n\n

对于Kotlin项目,令牌桶是简单性和效率之间的最佳平衡点。我们将实现它。

\n\n

编写代码:从零开始用Kotlin实现速率限制器(无需库)

\n\n

让我们创建一个简单但可用的速率限制器,使用协程和Mutex。这将是一个线程安全的令牌桶版本。

\n\n
import kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport kotlin.time.Duration\nimport kotlin.time.TimeSource\n\nclass TokenBucketRateLimiter(\n    private val maxTokens: Int,\n    private val refillRate: Long, // 每秒令牌数\n    private val timeSource: TimeSource = TimeSource.Monotonic\n) {\n    private var tokens: Long = maxTokens.toLong()\n    private var lastRefillTimestamp: Long = timeSource.markNow().elapsedNow().inWholeMilliseconds\n    private val mutex = Mutex()\n\n    suspend fun tryAcquire(): Boolean {\n        mutex.withLock {\n            refillTokens()\n            return if (tokens > 0) {\n                tokens--\n                true\n            } else {\n                

博客

书籍推荐