CircuitBreakerSwift 5.0.1

CircuitBreakerSwift 5.0.1

Mathieu BarnachonAndrew LeesSwift-at-IBMDanny Sung维护。



  • IBM

Build Status macOS Linux

CircuitBreaker

Circuit Breaker 设计模式用于提高应用程序的稳定性、改善响应时间并防止应用程序不断发出失败的请求。此库提供了将 Circuit Breaker 逻辑引入 Swift 应用程序所需的所有工具。

Circuit Breaker Diagram

内容

Swift 版本

CircuitBreaker 的最新版本与 Swift 二进制文件的 4.0.3 及更高版本兼容。您可以通过点击以下 链接 下载此版本的 Swift 二进制文件。

安装

要在您的 Swift 应用程序中利用 CircuitBreaker 包,您应该在 Package.swift 文件中指定对它的依赖。

 import PackageDescription

 let package = Package(
     name: "MySwiftProject",

     ...

     dependencies: [
         // Swift 4
         .package(url: "https://github.com/IBM-Swift/CircuitBreaker.git", .upToNextMajor(from: "5.0.0")),
         ...

     ])

使用

断路器状态基于超时和用户定义的故障(当你要断路的功能做出异步调用时非常有用)。

  1. 定义一个带有签名 (BreakerError, (fallbackArg1, fallbackArg2,...)) -> Void 的回退函数。
func myFallback(err: BreakerError, msg: String) {
    // The fallback will be called if one of the below occurs:
    //  1. The request does not return before the specified timeout
    //  2. CircuitBreaker is currently in Open state and set to fail fast.
    //  3. There was an error in the user's called context function (networking error, etc.)
    Log.verbose("Error: \(error)")
    Log.verbose("Message: \(msg)")
}
  1. 通过定义自己的错误处理来扩展 BreakerError,以便在上下文函数中使用。
extension BreakerError {
    public static let encodingURLError = BreakerError(reason: "URL could not be created")
    public static let networkingError = BreakerError(reason: "There was an error, while sending the request")
}
  1. 为你要断路的逻辑创建一个上下文函数(这允许你向断路器报告失败或成功)。请注意,上下文函数接收一个 Invocation 对象作为其参数。一个 Invocation 类的实例表示1)必须传递给上下文函数的参数类型,2)上下文函数执行的返回类型,以及3)用作回退闭包第二个参数的参数类型
func myContextFunction(invocation: Invocation<(String), String>) {
  let requestParam = invocation.commandArgs
  // Create HTTP request
  guard let url = URL(string: "http://myserver.net/path/\(requestParam)") else {
    // Something went wrong...

    ...

    invocation.notifyFailure(error: .encodingURLError)
  }

  var req = URLRequest(url: url)
  req.httpMethod = "GET"
  req.allHTTPHeaderFields = ["Content-Type": "application/json"]
  let session = URLSession.shared

  // Perform Request
  session.dataTask(with: req) { result, res, err in
    guard let result = result else {
      // Failed getting a result from the server

      ...

      invocation.notifyFailure(error: .networkingError)
      return
    }

    // Convert results to a JSON object
    let json = JSON(data: result)
    // Process JSON data

    ...

    invocation.notifySuccess()
  }.resume()
}
  1. 为每个你要断路的上下文函数(例如,端点)创建一个断路器实例。
let breaker = CircuitBreaker(command: myContextFunction, fallback: myFallback)
* Must specify the fallback function and the endpoint to circuit break
* Optional configurations include: timeout, resetTimeout, maxFailures, rollingWindow, and bulkhead
  1. 通过调用断路器的 run() 方法来调用端点的调用。你应该传递上下文命令和回退闭包的相应参数。在这个示例中,myContextFunction 以一个字符串作为其参数,而 myFallback 则具有作为第二个参数的字符串。
let id: String = ...
breaker.run(commandArgs: id, fallbackArgs: "Something went wrong.")
完整实现
...
extension BreakerError {
    public static let encodingURLError = BreakerError(reason: "URL could not be created")
    public static let networkingError = BreakerError(reason: "There was an error, while sending the request")
}

func myFallback(err: BreakerError, msg: String) {
    // The fallback will be called if one of the below occurs:
    //  1. The request does not return before the specified timeout
    //  2. CircuitBreaker is currently in Open state and set to fail fast.
    //  3. There was an error in the user's called context function (networking error, etc.)
    Log.verbose("Error: \(error)")
    Log.verbose("Message: \(msg)")
}

func myContextFunction(invocation: Invocation<(String), String>) {
  let requestParam = invocation.commandArgs
  // Create HTTP request
  guard let url = URL(string: "http://mysever.net/path/\(requestParam)") else {
    // Something went wrong...

    ...

    invocation.notifyFailure(error: .encodingURLError)
  }

  var req = URLRequest(url: url)
  req.httpMethod = "GET"
  req.allHTTPHeaderFields = ["Content-Type": "application/json"]
  let session = URLSession.shared

  // Perform Request
  session.dataTask(with: req) { result, res, err in
    guard let result = result else {
      // Failed getting a result from the server

      ...

      invocation.notifyFailure(error: .networkingError)
      return
    }

    // Convert results to a JSON object
    let json = JSON(data: result)
    // Process JSON data

    ...

    invocation.notifySuccess()
  }.resume()
}

let breaker = CircuitBreaker(command: myContextFunction, fallback: myFallback)

let id: String = ...
breaker.run(commandArgs: id, fallbackArgs: "Something went wrong.")

...

API

断路器

构造函数

CircuitBreaker(timeout: Int = 1000, resetTimeout: Int = 60000, maxFailures: Int = 5, rollingWindow: Int = 10000, bulkhead: Int = 0, command: @escaping AnyContextFunction<A>, fallback: @escaping AnyFallback<C>)

构造函数参数

  • timeout 函数应在多少毫秒内完成,否则调用被视为失败。默认设置为1000毫秒。
  • resetTimeout 在设置为半开状态之前等待的毫秒数。默认设置为60000毫秒。
  • maxFailures 在将状态设置为开启之前,在滚动窗口内允许的失败次数。默认设置为5。
  • rollingWindow 电路触发时必须发生的最大失败次数的时间窗口,单位为毫秒。例如,如果maxFailures为5,rollingWindow为10000毫秒,则电路要触发,必须在10秒内发生5次调用失败,即使这些失败不是连续的。默认设置为10000毫秒。
  • bulkhead 同时运行请求的限制数量。默认设置为0,相当于没有使用bulkheading功能。
  • fallback 用户指定的用于表示超时或快速失败的函数。所需的格式:(BreakerError, (fallbackArg1, fallbackArg2,...)) -> Void
  • command 用于电路断开的上下文函数,允许用户定义失败(上下文提供了对相应的电路断开实例的间接引用)。

统计

追踪统计

  • 总请求次数
  • 并发请求量
  • 拒绝请求
  • 成功响应
  • 平均执行响应时间
  • 平均总响应时间
  • 失败响应
  • 总超时
  • 总延迟
  • 总执行延迟
  • Hystrix 兼容快照
...
// Create CircuitBreaker
let breaker = CircuitBreaker(command: myFunction, fallback: myFallback)

// Invoke breaker call
breaker.run(commandArgs: (a: 10, b: 20), fallbackArgs: "Something went wrong.")

// Log Stats snapshot
breaker.snapshot()

// Hystrix compliant snapshot
let snapshot = breaker.snapshot
...

观察统计

CircuitBreaker库提供了一个接口,用于观察新的CircuitBreaker实例以注册和跟踪统计变化。在CircuitBreaker实例的初始化过程中,通知链接的监视器其实例化,从而允许它们开始跟踪实例的统计信息。CircuitBreaker实例向监视器提供了一个Hystrix兼容的统计快照,然后可以进行相应的处理。

/// Initialize stat monitors
let monitor1 = SwiftMetrics()
let monitor2 = ...
...
let monitorN = ...

/// Register monitors
CircuitBreaker.addMonitor(monitor1)
CircuitBreaker.addMonitor(monitor2)
...
CircuitBreaker.addMonitor(monitorN)

// Create instances of CircuitBreaker
let circuit1 = CircuitBreaker()
let circuit2 = CircuitBreaker()
...
let circuitN = CircuitBreaker()

如上所述,初始化器负责通知每个监视器新的CircuitBreaker实例。

许可证

此Swift包采用Apache 2.0许可证。完整的许可证文本可在LICENSE中查看。