首页  >  文章  >  后端开发  >  Go 应用程序中的断路器

Go 应用程序中的断路器

PHPz
PHPz原创
2024-08-27 21:31:56679浏览

如今,我们的应用程序依赖其他应用程序是很常见的,特别是当我们在微服务环境中工作时。我们的应用程序开始报告错误是很常见的,在调查时,我们注意到合作伙伴团队或供应商的某些 API 已关闭。

提高应用程序弹性的一个好做法是切断与那些处于弃用状态的应用程序的通信。观察其他领域,我们吸收了电气工程中断路器的概念。其中放置了一个设备或断路器,如果发生故障,它会自动关闭。这在我们的家里很常见,如果电网开始变得不稳定,断路器会自动关闭。

在计算中,我们的断路器稍微复杂一些,因为我们还定义了一个中间状态。下图更好地解释了断路器的工作原理:

Circuit Breaker em aplicações Go

最后,这些州是:

  • open:应用程序之间没有通信。达到此状态后,计时器开始为重置服务留出时间。在计时器结束时,我们过渡到半开。
  • 关闭:应用程序之间存在通信。对于每个失败的请求,都会更新计数器。如果达到故障极限,我们会将电路切换为开路。
  • half-open:恢复状态,直到通信完全畅通为止。其中,成功计数器会根据每个请求进行更新。如果达到理想的成功次数,我们会将电路移至闭合状态。如果请求失败,我们将恢复开放状态。

很酷,对吧?但为了更好地体现这个概念,我们在实践中如何做呢?

首先,我们来构建我们的服务A,它将负责接收请求,也就是说,它将是我们的应用程序所依赖的服务,供应商的服务等。为了方便起见,我们将公开两个端点,一个始终返回 200 的 /success 和一个始终返回 500 的 /failure。

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/success", func(w http.ResponseWriter, r *http.Request) { 
    w.WriteHeader(http.StatusOK) })
    http.HandleFunc("/failure", func(w http.ResponseWriter, r *http.Request) { 
    w.WriteHeader(http.StatusInternalServerError) })

    fmt.Println("Server is running at http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

服务 B 将负责调用服务 A。它将构建我们的断路器。对我们来说幸运的是,Go 社区已经有了实现该模式的 gobreaker 库!首先,我们定义断路器的属性:

var st gobreaker.Settings
st.Name = "Circuit Breaker PoC"
st.Timeout = time.Second * 5
st.MaxRequests = 2
st.ReadyToTrip = func(counts gobreaker.Counts) bool {
    return counts.ConsecutiveFailures >= 1
}

虽然该库允许我们定制更多的东西,但我们将重点关注三个:

  • 超时:电路保持开路状态的时间。在我们的例子中,时间设置为 5 秒。
  • MaxRequests:关闭前成功请求的数量。在我们的示例中,我们将其设置为 2 个请求。
  • ReadyToTrip:定义从关闭过渡到打开的条件。为了方便起见,我们假设一次失败就足够了。

然后我们可以初始化断路器并发出请求:

cb := gobreaker.NewCircuitBreaker[int](st)

url := "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // closed!

url = "http://localhost:8080/failure"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // open!

time.Sleep(time.Second * 6)
url = "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // half-open!

url = "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // closed!

我们可以注意到 gobreaker 作为函数的包装器。如果函数返回错误,则会增加错误数量,如果没有,则会增加成功数量。那么让我们定义这个函数:

func Get(url string) (int, error) {
    r, _ := http.Get(url)

    if r.StatusCode != http.StatusOK {
        return r.StatusCode, fmt.Errorf("failed to get %s", url)
    }

    return r.StatusCode, nil
}

我们的 Go 服务使用了断路器!通过使用此模式,您可以提高服务的弹性和容错能力。我们可以注意到,在使用该库时,复杂性被完全抽象,使得将其集成到我们日常生活中的过程非常简单。如果您想查看完整的概念验证代码,请转到此处。

如果您想了解其他弹性模式,Elton Minetto 发表了一篇关于该主题的精彩文章!

请在评论中告诉我你对这篇文章的看法,这里有一个问题:你以前使用过断路器吗?哦,你也可以在我的个人博客找到我!

以上是Go 应用程序中的断路器的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn