SwiftUI Rate Limiting & Backpressure(保护你的后端免受自己的应用侵害)
Source: Dev.to
大多数应用都会这样调用 API:
try await api.fetchFeed()
这本来没问题……直到某些情况一次触发了大量请求。
示例
- 无限刷新循环
- 过于激进的后台同步
- 多个视图请求相同的数据
- 网络恢复后出现的重试风暴
- 多台设备同时同步
突然之间,你的应用成了自己后端最大的威胁。
🧠 核心原则
一个健康的系统会控制其请求速率。即使在一切正常运行时,失控的流量也可能导致 API 过载。
🧱 1. 什么是速率限制?
速率限制控制在一个时间窗口内可以发生的请求数量。
示例规则
10 requests per second
如果有更多请求到达,它们必须 等待、排队,或被 拒绝。这可以保护后端和客户端设备。
🧬 2. 令牌桶模型
一种常见的方法是令牌桶算法。
概念
Bucket contains tokens
Each request consumes one token
Tokens refill over time
如果桶为空,请求必须等待。
🧱 3. 简单速率限制器实现
actor RateLimiter {
private let maxTokens: Int
private let refillInterval: TimeInterval
private var tokens: Int
private var lastRefill: Date
init(maxTokens: Int, refillInterval: TimeInterval) {
self.maxTokens = maxTokens
self.refillInterval = refillInterval
self.tokens = maxTokens
self.lastRefill = Date()
}
func acquire() async {
refill()
while tokens == 0 {
try? await Task.sleep(nanoseconds: 100_000_000) // 0.1 s
refill()
}
tokens -= 1
}
private func refill() {
let now = Date()
let elapsed = now.timeIntervalSince(lastRefill)
if elapsed >= refillInterval {
tokens = maxTokens
lastRefill = now
}
}
}
这确保请求以受控的速度进行。
🚦 4. 使用速率限制器
Wrap your API calls:
let limiter = RateLimiter(maxTokens: 5, refillInterval: 1)
func fetchFeed() async throws -> Feed {
await limiter.acquire()
return try await api.fetchFeed()
}
Now your app cannot exceed 5 requests per second.
🔄 5. 什么是背压?
背压防止系统产生超过其处理能力的工作负载。
场景
Scroll view triggers 50 image loads
没有背压:立即发起 50 个网络请求。
有背压:请求排队并逐步执行。
这可以保护 CPU、内存、网络和后端 API。
📦 6. 请求队列模式
actor RequestQueue {
private var queue: [() async -> Void] = []
func enqueue(_ task: @escaping () async -> Void) {
queue.append(task)
processNext()
}
private func processNext() {
guard !queue.isEmpty else { return }
let task = queue.removeFirst()
Task {
await task()
processNext()
}
}
}
该模式确保对排队任务进行受控执行。
🔋 7. 为什么移动应用需要限流
移动环境不可预测。常见问题包括:
- API 配额
- 缓慢的蜂窝连接
- 后台同步突发
- 多标签页刷新循环
如果没有限制,后端会被淹没,电池消耗更快,UI 也会变得不稳定。限流可以使系统保持稳定。
🌐 8. 结合断路器
Circuit Breakers → stop requests during failures
Rate Limiting → control requests during success
它们一起形成完整的网络弹性。
🧪 9. 测试速率限制
测试场景
- 快速刷新循环
- 大滚动列表触发网络请求
- 后台同步突发
- 重试风暴
验证以下内容
- 请求速率保持稳定
- 队列正确处理
- 不存在后端过载
⚠️ 10. 常见反模式
避免:
- 无限并行请求
- 失败后立即重试
- 每个视图单独进行网络请求且未协调
- 忽视服务器速率限制
- 未对相同请求进行去重
这些会导致请求风暴、API 被封禁、耗电以及后端不稳定。
🧠 思维模型
User Action
→ Request Queue
→ Rate Limiter
→ Network Request
→ API
注意:“立即触发每个请求”。
🚀 最终思考
- 可预测的网络行为
- 后端保护
- 更平稳的性能
- 更佳的电池使用
- 生产级弹性