CircuitBreaker
Circuit Breaker 设计模式用于提高应用稳定性,改善响应时间,并防止应用不断发出失败请求。此库提供了将 Circuit Breaker 逻辑引入 Swift 应用程序所需的工具。
Swift 版本
CircuitBreaker 的最新版本与 Swift 二进制文件(版本 4.1.2 及更高版本)兼容。您可以通过以下链接下载此版本的 Swift 二进制文件:链接。
入门指南
将 CircuitBreaker
添加到您应用程序 Package.swift
文件中的依赖项。将 "x.x.x"
替换为最新的 CircuitBreaker
发布版本。
.package(url: "https://github.com/IBM-Swift/CircuitBreaker.git", from: "x.x.x")
将 CircuitBreaker
添加到您的目标的依赖项
.target(name: "example", dependencies: ["CircuitBreaker"]),
在您的应用程序内部导入该包
import CircuitBreaker
使用方法
CircuitBreaker 状态基于超时和用户定义的失败(当您要断路转换的函数进行异步调用时非常有用)。要在您的应用程序代码中使用 CircuitBreaker,您需要执行以下操作
- 定义一个具有签名
(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: \(err)")
Log.verbose("Message: \(msg)")
}
- 通过定义您自己的错误处理来扩展 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")
public static let jsonDecodingError = BreakerError(reason: "Could not decode result into JSON")
}
- 为你要执行熔断的逻辑创建一个上下文函数(这样你可以向熔断器报告失败或成功)。上下文函数接收一个
Invocation
对象作为其参数。一个Invocation
类的实例表示以下内容- 必须传递给上下文函数的参数类型。
- 上下文函数执行后的返回类型。
- 用作后备闭包第二个参数的参数类型。
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)
return
}
var req = URLRequest(url: url)
req.httpMethod = "GET"
let session = URLSession.shared
// Perform the 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
guard let json = (try? JSONSerialization.jsonObject(with: result, options: [])) as? [String: Any] else {
invocation.notifyFailure(error: .jsonDecodingError)
return
}
// Process JSON data
invocation.notifySuccess()
}.resume()
}
- 为每个你想进行熔断的上下文函数(例如端点)创建一个CircuitBreaker实例。熔断器实例必须指定熔断器的名称、要熔断的端点和后备函数。可选的配置包括:group、timeout、resetTimeout、maxFailures、rollingWindow和bulkhead,有关这些配置选项的详细信息,请参见API参考。
let breaker = CircuitBreaker(name: "Circuit1", command: myContextFunction, fallback: myFallback)
- 通过调用CircuitBreaker的
run()
方法来调用端点的调用。你应该为上下文命令和后备闭包传递相应的参数。在此示例中,myContextFunction
以字符串作为其参数,而myFallback
以字符串作为第二个参数。
let requestParam: String = "myRequestParams"
breaker.run(commandArgs: requestParam, 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")
public static let jsonDecodingError = BreakerError(reason: "Could not decode result into JSON")
}
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: \(err)")
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"
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
guard let json = (try? JSONSerialization.jsonObject(with: result, options: [])) as? [String: Any] else {
invocation.notifyFailure(error: .jsonDecodingError)
return
}
// Process JSON data
invocation.notifySuccess()
}.resume()
}
let breaker = CircuitBreaker(name: "Circuit1", command: myContextFunction, fallback: myFallback)
let requestParam: String = "myRequestParams"
breaker.run(commandArgs: requestParam, fallbackArgs: "Something went wrong.")
统计数据
以下统计数据将被跟踪以记录熔断器实例
跟踪统计
- 总请求数
- 并发请求
- 拒绝请求
- 成功响应
- 平均执行响应时间
- 平均总响应时间
- 失败响应
- 总超时时间
- 总延迟
- 总执行延迟
- Hystrix兼容快照
以下示例代码显示如何记录统计信息的快照以及如何创建Hystrix兼容的快照。
// Create CircuitBreaker
let breaker = CircuitBreaker(name: "Circuit1", command: myFunction, fallback: myFallback)
// Invoke breaker call
breaker.run(commandArgs: (a: 10, b: 20), fallbackArgs: "Something went wrong.")
// Log statistics snapshot
breaker.logSnapshot()
// Hystrix compliant snapshot
let snapshot = breaker.snapshot
观察数据统计
CircuitBreaker 库提供了一个用于观察新的 CircuitBreaker 实例的接口,以便注册和跟踪统计信息的变化。在 CircuitBreaker 实例的初始化过程中,连接的监控器会接到其实例化的通知,从而使它们开始跟踪该实例的统计信息。CircuitBreaker 实例向监控器公开了一个与 Hystrix 兼容的统计快照,监控器可以据此进行处理。有关更多信息,请参阅 API 文档。
API 文档
欲了解更多信息,请访问我们的 API 参考文档。
社区
我们热爱讨论服务器端 Swift 和 Kitura,加入我们的 Slack 与团队见面吧!
许可证
此 Swift 包受 Apache 2.0 许可的许可。完整的许可文本可在 LICENSE 中找到。