客户端容错,服务端容错机制
在分布式系统中,故障是不可避免的。 最终,某些服务将陷入瘫痪,因此无法足够Swift地做出响应,或者更糟的是,服务将简单地死掉。 除非所有服务在隔离时都牢记在心,否则依赖降级(或失效!)服务的服务自然会受到影响,并可能在整个系统中级联不稳定。
Netflix经历了烦恼的分布式系统故障,并将Hystrix开发为Circuit Breaker模式的复杂实现。 简而言之,Hystrix是:
延迟和容错库,旨在隔离对远程系统,服务和第三方库的访问点,停止级联故障,并在不可避免发生故障的复杂分布式系统中实现弹性。
Github
实际上,Hystrix在Netflix上得到了充分利用,并且:
每天通过Hystrix在Netflix上执行数百亿个线程隔离和数千亿个信号量隔离的调用,并且通过使用它,可以实现正常运行时间和弹性的显着改善。
Github
用Java实现 Hystrix时 ,可以使用其他实现。 实际上,如果您要在Go中构建服务,那么您肯定会想要看一下hystrix-go ,顾名思义,这是一种非常容易使用的Hystrix Go实现。
例如,以下是一些查询第三方服务的Go代码(在本例中为JIRA,以获取未解决的问题):
简单的功能来获取未解决的问题
func retrieveOpenIssues(userConfig UserDefinedConfig) JiraSearchResponse {
url := fmt.Sprintf("%s/rest/api/2/search", userConfig.Host)
query := fmt.Sprintf("project = %s AND resolution = Unresolved ORDER BY priority DESC", userConfig.Project)
fields := []string{"summary", "key", "status", "assignee", "description", "issuetype", "created"}
jsonStr, _ := json.Marshal(JiraSearchRequest{query, 0, fields})
responseChannel := make(chan JiraSearchResponse)
go func() {
body := httpRequest(url, "POST", jsonStr, userConfig)
var searchRes JiraSearchResponse
json.Unmarshal(body, &searchRes)
responseChannel <- searchRes
}()
return <-responseChannel
}
在JIRA开始出现问题之前 ,这段代码已经足够好用了,在这种情况下,超时最终将在调用代码内创建一个非常讨厌的错误。 实际上,Go 内置的恐慌将会发生:
惊慌失措!
panic: Post https://jira.acme.com/rest/api/2/search: dial tcp 94.144.0.13:443: i/o timeout
goroutine 24 [running]:
runtime.panic(0x325620, 0xc208024bd0)
/usr/local/go/src/pkg/runtime/panic.c:279 +0xf5
...
当然,我可以自然地内置一些类似于警卫的代码来处理这些错误,或者可以使用以Hystrix建模的框架为我完成此任务。 因为我很懒 ,所以我倾向于利用他人的专业知识,hystrix-go也不例外!
当然,您首先需要导入hystrix-go:
导入hystrix-go
import "github.com/afex/hystrix-go/hystrix"
hystrix-go在代码中实现起来非常简单–您只需在hystrix.Go
函数( 内部创建 goroutine )中实现逻辑即可。 此外,您将创建一个后备函数,该函数会在错误条件( 例如超时等 )下调用。
hystrix-go超级简单易用
hystrix.Go("some command", func() error {
// normal path code
return nil
}, func(err error) error {
// do this when errors occur
return nil
})
注意, hystrix.Go
函数hystrix.Go
三个参数,最后两个是匿名函数 :第一个是您的常规逻辑,最后一个是所需的后备逻辑。 您自然可以使用Go Channel接收来自任一功能的数据。
string
first参数是一个键,可以与唯一配置匹配。 例如,您可以在回退开始之前设置各个超时,甚至设置错误阈值。
hystrix-go也易于配置
hystrix.ConfigureCommand("unique_command", hystrix.CommandConfig{
Timeout: 1000,
MaxConcurrentRequests: 100,
ErrorPercentThreshold: 25,
})
在上述情况下,超时时间以毫秒为单位; 因此,命令unique_command
将在1秒后触发回退条件。 该库已经过良好的测试,我建议阅读一些测试以查看触发的各种后备情况。
掌握了如何添加后备和配置与之相关的任何条件的知识,因此在任何go代码中合并合法的容错功能非常容易。 例如,我可以使用JIRA调用代码并将其包装在hystrix.Go
函数中,如下所示:
现在有了更多Hystrix!
func retrieveOpenIssues(userConfig UserDefinedConfig) JiraSearchResponse {
url := fmt.Sprintf("%s/rest/api/2/search", userConfig.Host)
query := fmt.Sprintf("project = %s AND resolution = Unresolved ORDER BY priority DESC", userConfig.Project)
fields := []string{"summary", "key", "status", "assignee", "description", "issuetype", "created"}
jsonStr, _ := json.Marshal(JiraSearchRequest{query, 0, fields})
responseChannel := make(chan JiraSearchResponse)
hystrix.ConfigureCommand("Get all Issues", hystrix.CommandConfig{Timeout: 2000})
hystrix.Go("Get all Issues", func() error {
body := httpRequest(url, "POST", jsonStr, userConfig)
var searchRes JiraSearchResponse
json.Unmarshal(body, &searchRes)
responseChannel <- searchRes
return nil
}, func(err error) error {
var searchRes JiraSearchResponse
responseChannel <- searchRes
return nil
})
return <-responseChannel
}
在这种情况下,我还指定了“获取所有问题”键功能将在2秒后触发超时。 我将利用HTTP GET
常规逻辑放在第一个匿名函数的内部,而我的后备逻辑放在第二个匿名函数的内部。 此后备逻辑只是返回一个空响应,以免完全影响依赖某种响应的下游服务。 也就是说,如果JIRA慢,慢或者只是没有响应,而不是产生了恐慌,则返回空响应。 表面上这禁止了级联的错误链。
当然,如果认为必要,我的后备代码可以执行其他逻辑; 而且,我的后备代码可以检测到生成的错误类型。 例如,相对于并发问题,我的后备代码在超时情况下的React可能会有所不同。 可以很容易地查询传递到回退功能中的error
对象: if err.Error() == "max concurrency"
。
Netflix已在大型,高度分布式的系统中使用Hystrix取得了成功 。 如果您想在任何Go服务中建立容忍度,那么我就不建议您过多地使用hystrix:它的设置和使用都非常容易。
翻译自: https://www.javacodegeeks.com/2015/02/fault-tolerance-go.html
客户端容错,服务端容错机制