Go 应用程序中的断路器
如今,我们的应用程序依赖其他应用程序是很常见的,特别是当我们在微服务环境中工作时。我们的应用程序开始报告错误是很常见的,在调查时,我们注意到合作伙伴团队或供应商的某些 API 已关闭。
提高应用程序弹性的一个好做法是切断与那些处于弃用状态的应用程序的通信。观察其他领域,我们吸收了电气工程中断路器的概念。其中放置了一个设备或断路器,如果发生故障,它会自动关闭。这在我们的家里很常见,如果电网开始变得不稳定,断路器会自动关闭。
在计算中,我们的断路器稍微复杂一些,因为我们还定义了一个中间状态。下图更好地解释了断路器的工作原理:
最后,这些州是:
- 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中文网其他相关文章!

热AI工具

Undress AI Tool
免费脱衣服图片

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Stock Market GPT
人工智能驱动投资研究,做出更明智的决策

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Goprovidesbuilt-insupportforhandlingenvironmentvariablesviatheospackage,enablingdeveloperstoread,set,andmanageenvironmentdatasecurelyandefficiently.Toreadavariable,useos.Getenv("KEY"),whichreturnsanemptystringifthekeyisnotset,orcombineos.Lo

UseGomodulesbyrunninggomodinittocreateago.modfile,whichmanagesdependenciesandversions.2.Organizecodeintopackageswhereeachdirectoryisapackagewithaconsistentpackagename,preferablymatchingthedirectoryname,andstructureimportsbasedonthemodulepath.3.Import

使用Go泛型和container/list可实现线程安全的LRU缓存;2.核心组件包括map、双向链表和互斥锁;3.Get和Add操作均通过锁保证并发安全,时间复杂度为O(1);4.当缓存满时自动淘汰最久未使用的条目;5.示例中容量为3的缓存添加4个元素后成功淘汰最久未使用的"b"。该实现完整支持泛型、高效且可扩展。

在Go中,创建和使用自定义错误类型能提升错误处理的表达力和可调试性,答案是通过定义实现Error()方法的结构体来创建自定义错误,例如ValidationError包含Field和Message字段并返回格式化错误信息,随后可在函数中返回该错误,通过类型断言或errors.As检测具体错误类型以执行不同逻辑,还可为自定义错误添加行为方法如IsCritical,适用于需结构化数据、差异化处理、库导出或API集成的场景,而简单情况可用errors.New,预定义错误如ErrNotFound可用于可比

customBuildTagsingoallowConditionalCompilationBasedOneNennvironment,架构,orcustomscenariosbyusing // go:buildtagsatthetopoffiles,watheretheneeneeneeneenabledviagobuild-tags“ tagname”

Go应用中处理信号的正确方式是使用os/signal包监听信号并执行优雅关闭,1.使用signal.Notify将SIGINT、SIGTERM等信号发送到通道;2.在goroutine中运行主服务并阻塞等待信号;3.收到信号后通过context.WithTimeout执行带超时的优雅关闭;4.清理资源如关闭数据库连接、停止后台goroutine;5.必要时用signal.Reset恢复默认信号行为,确保程序在Kubernetes等环境中能可靠终止。

Tohandlepanicsingoroutines,usedeferwithrecoverinsidethegoroutinetocatchandmanagethemlocally.2.Whenapanicisrecovered,logitmeaningfully—preferablywithastacktraceusingruntime/debug.PrintStack—fordebuggingandmonitoring.3.Onlyrecoverfrompanicswhenyoucanta

本文深入探讨了Go语言中如何区分IEEE 754标准浮点数中的正零( 0)和负零(-0)。通过分析math包中的Signbit函数,并结合实际代码示例,详细阐述了识别负零的正确方法。文章旨在帮助开发者理解浮点数零值的特性,并掌握在Go语言中精确处理这些特殊数值的技巧,确保在序列化或特定计算场景下符号信息的完整性。
