Go:多錯誤管理

由 Renee French 創作的原始 Go Gopher 製作的 “Go 的旅程” 插圖。

Go 語言中的錯誤(error)管理總是能引起爭論,同時,在關於使用 Go 語言的時候,開發者面對最大的挑戰的年度調查 [1] 中也是一個經常性的話題。然而,在併發環境處理 error 的場景下,或者在同一個 goroutine 中合併多個錯誤的場景下,Go 提供了很不錯的包可以讓多個錯誤的處理變得簡單:來看看如何合併由單個 goroutine 生成的多個 error。

一個 goroutine,多個 error

當編寫有着重試策略的代碼時,將多個 error 合併爲一個會十分有用,比如,下面是我們需要收集生成的 error 的一個基本例子:

這個程序讀取並解析一個 CSV 文本,並且展示發現的錯誤。如果將 error 聚合爲一個完整的報告,會更加方便。爲了將錯誤合併爲一個,我們可以在兩個不錯的包中進行選擇:

之後可以打印出一個報告:

這裏的實現是類似的,這是輸出:

error 通過分號連接,沒有經過其他格式化。

關於兩個包的性能,這是一個使用相同程序,有着更高次數失敗的基準測試:

name                    time/op         alloc/op        allocs/op
HashiCorpMultiErrors-4  6.01µs ± 1%     6.78kB ± 0%     77.0 ± 0%
UberMultiErrors-4       9.26µs ± 1%     10.3kB ± 0%      126 ± 0%

Uber 的實現略慢,同時消耗更多內存。但是,這個包被設計爲一次將錯誤聚合在一起,而不是每次都追加它們。在聚合 error 的時候,結果是接近的。但是由於需要額外步驟,代碼有點不太優雅。這是新的結果:

name                    time/op         alloc/op        allocs/op
HashiCorpMultiErrors-4  6.01µs ± 1%     6.78kB ± 0%     77.0 ± 0%
UberMultiErrors-4       6.02µs ± 1%     7.06kB ± 0%     77.0 ± 0%

兩個包都通過在自定義實現中實現了 Error() string 函數的方式利用了 Go 的 error 接口。

一個 error,多個 goroutine

在操作多個 goroutine 來處理一個任務的時候,爲了保證程序的正確性,正確地管理結果和錯誤彙總是有必要的。

以一個程序開始,該程序使用多個 goroutine 執行一系列行爲(action);每個行爲持續一秒:

爲了描繪 error 傳播,第三個 goroutine 的第一個 action 會失敗。這是發生的事情:

如同預期的一樣,這個程序大致用了三秒鐘,因爲大多數 goroutine 需要經歷三個 action,每一個需要一秒:

go run .  0.30s user 0.19s system 14% cpu 3.274 total

然而,我們可能希望使 goroutine 之間相互依賴,並且如果其中一個失敗就取消他們。避免無謂工作的解決方案可以是加一個 context,並且,一旦一個 goroutine 失敗,就會取消它:

這恰好就是 errgroup[6] 所提供的;當處理一組 goroutine 的時候,一個錯誤以及上下文傳播。這是使用 errgroup[7] 包的新代碼:

由於通過 error 傳播了取消的上下文,這個程序現在運行地更快了:

go run .  0.30s user 0.19s system 38% cpu 1.269 total

這個包所帶來的其他好處是,我們不需要再操心等待組的增加以及將 goroutine 標記爲已完成。這個包爲我們管理了這些,我們僅僅需要說明什麼時候我們準備好了等待過程的結束。

via: https://medium.com/a-journey-with-go/go-multiple-errors-management-a67477628cf1

作者:Vincent Blanchon[8] 譯者:dust347[9] 校對:polaris1119[10]

本文由 GCTT[11] 原創編譯,Go 中文網 [12] 榮譽推出,發佈在 Go 語言中文網公衆號,轉載請聯繫我們授權。

參考資料

[1] 年度調查: https://blog.golang.org/survey2019-results

[2] HashiCorp: https://github.com/hashicorp

[3] go-multierror: https://github.com/hashicorp/go-multierror

[4] Uber: https://github.com/uber-go

[5] multierr: https://github.com/uber-go/multierr

[6] errgroup: https://pkg.go.dev/golang.org/x/sync/errgroup?tab=doc

[7] errgroup: https://pkg.go.dev/golang.org/x/sync/errgroup?tab=doc

[8] Vincent Blanchon: https://medium.com/@blanchon.vincent

[9] dust347: https://github.com/dust347

[10] polaris1119: https://github.com/polaris1119

[11] GCTT: https://github.com/studygolang/GCTT

[12] Go 中文網: https://studygolang.com/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Ro5uk47KJhwnD0h6nEF7NA