SwiftUI 断路器与网络弹性模式(防止级联故障)

发布: (2026年3月8日 GMT+8 04:48)
5 分钟阅读
原文: Dev.to

Source: Dev.to

大多数应用都会这样调用 API:

try await api.fetchUser()

这样可以正常工作……直到服务器开始出现故障。此时你的应用可能会:

  • 立即重试
  • 发送数百个失败的请求
  • 消耗电池电量
  • 给后端造成过载
  • 使 UI 变慢
  • 加剧故障的影响

这被称为 cascading failure(级联故障)。生产环境的应用需要相应的保护机制。

🧠 核心原则

当系统出现故障时,停止让情况更糟。系统应当检测到故障并暂停请求,而不是无休止地重试。

🧱 1. 什么是断路器?

断路器可以防止对出现故障的服务进行重复调用。它有三种状态:

Closed → Normal operation
Open → Requests blocked
Half-Open → Testing recovery

流程

Requests Fail
→ Circuit Opens
→ Requests Blocked
→ Recovery Test
→ Circuit Closes

🔌 2. 定义电路状态

enum CircuitState {
    case closed
    case open(until: Date)
    case halfOpen
}
  • Closed → 允许请求
  • Open → 暂时阻止请求
  • Half‑Open → 允许有限请求以测试恢复

🧬 3. 基本断路器实现

final class CircuitBreaker {
    private(set) var state: CircuitState = .closed
    private var failureCount = 0
    private let failureThreshold = 5
    private let timeout: TimeInterval = 30

    func recordFailure() {
        failureCount += 1
        if failureCount >= failureThreshold {
            state = .open(until: Date().addingTimeInterval(timeout))
        }
    }

    func recordSuccess() {
        failureCount = 0
        state = .closed
    }
}

故障会累计,直至断路器打开。

🚦 4. 当电路打开时阻止请求

在执行请求之前:

func canExecute() -> Bool {
    switch state {
    case .closed:
        return true
    case .open(let until):
        return Date() > until
    case .halfOpen:
        return true
    }
}

用法

guard breaker.canExecute() else {
    throw NetworkError.circuitOpen
}

这可以保护后端。

🔄 5. 转为半开

当超时时间到期时:

case .open(let until):
    if Date() > until {
        state = .halfOpen
        return true
    }

只有少量请求应当用于测试恢复。如果它们成功 → 关闭电路;如果它们失败 → 重新打开。

🔋 6. 为什么移动应用需要断路器

移动网络不稳定。常见的故障场景包括:

  • 服务器宕机
  • DNS 问题
  • TLS 失败
  • 限流
  • 蜂窝网络丢包

如果没有断路器,您的应用可能会:

  • 向后端发送大量请求
  • 快速耗尽电池
  • 放大故障影响

断路器可以防止这些问题。

🧱 7. 与 API 客户端集成

包装网络调用:

func performRequest(_ operation: () async throws -> T) async throws -> T {
    guard breaker.canExecute() else {
        throw NetworkError.circuitOpen
    }

    do {
        let result = try await operation()
        breaker.recordSuccess()
        return result
    } catch {
        breaker.recordFailure()
        throw error
    }
}

这使得弹性自动化。

🌐 8. 与重试策略相结合

断路器可以与重试一起使用。示例流程:

Request
→ Failure
→ Retry (exponential backoff)
→ Failure threshold reached
→ Circuit opens
→ Requests paused

这可以防止重试风暴。

🧪 9. 测试断路器

Test scenarios:

  • repeated server errors
  • timeout storms
  • network disconnections
  • recovery after outage
  • half‑open transition

Verify that:

  • requests stop during outages
  • requests resume after recovery

⚠️ 10. 常见反模式

Avoid:

  • infinite retries
  • retrying immediately after failure
  • ignoring server rate limits
  • retrying while offline
  • not tracking failure counts

These cause backend overload, cascading outages, and battery drain.

🧠 Mental Model

Request
 → Failure Detection
   → Circuit Breaker
     → Pause Requests
       → Recovery Probe
         → Resume Traffic

不要:“仅仅再次重试”。

🚀 最终思考

断路器为您的应用提供:

  • 后端保护
  • 更智能的重试行为
  • 降低电池消耗
  • 在故障期间的弹性
  • 可预测的恢复
0 浏览
Back to Blog

相关文章

阅读更多 »

移动开发中最危险的消息

如果你开发移动应用,可能至少见过一次这样的提示:“嘿……构建没有安装”。就这样,你的一天被毁了。你…