網站首頁 編程語言 正文
引言
我看到很多 golang
社區的開發者,特別是因為它的簡單性而被吸引的開發者,對 golang
中的事情應該如何處理做出了一些快速的判斷。
其中一件事就是錯誤處理。由于目前大多數語言的開發者都來自于 OOP
背景,他們習慣于處理異常,或者說"拋出"異常的概念來停止當前的應用程序的流程,而且他們大多認為這也是 golang
的方式,我們必須在出錯的情況下停止我們的應用程序的流程。他們錯了!
不要濫用你的工具
我見過很多,我以前也是這樣做的。每當有意外情況發生時,就用 os.exit(1)
,然后繼續前進。好吧,這不是使用go的正確方法!
我明白為什么這被廣泛使用,因為大多數早期的 golang
應用程序只是終端工具,而且許多這些工具曾經使用 .sh
可執行文件來構建,我們曾經只是退出1;以表示剛剛發生了一些意外的事情,我們想退出。
我們把這種習慣帶到了我們的 golang 簡單的終端應用中,然后又帶到了復雜的應用中,這只是另一種 Cargo Cult Programming。 我高度鼓勵你在不得不這樣做的情況下,要非常小心,因為它是:
- 隨著你的應用程序的增長,非常難以維護。
- 最重要的是,不可能對這樣的代碼進行單元測試,這顯然表明它的不潔性。
- 以這種方式退出將阻止你的任何延遲操作的執行,你的程序將立即終止,這可能導致資源泄漏。請考慮一下這個例子:
func main() { dbConnection := db.connect("...") defer dbConnection.Close() // this operation won't be executed! entity := Entity{} err := dbConnection.Save(entity) if err != nil { os.Exit(1) } }
考慮傳遞你的錯誤
錯誤只是 golang
中的另一種類型,你必須用它們來控制程序的執行流程。
為了做到這一點,我們必須在整個程序中傳播這些錯誤,直到適當的處理點。
考慮一個管理訂單的HTTP API,我們想禁止客戶在特定條件下下訂單,例如:
package order // package errors var ( UnableToShipToCountry = errors.New("unable to ship order to the requested country") ) type Order struct { // ... order fields } type OrderRepo struct { DB db // ... } func newOrderFromRequest(o OrderRequest) (Order, error) { if o.ShippingAddress.Country != "DE" { return UnableToShipToCountry } // ... the creation logic return Order{...}, nil } func (r *OrderRepo)PlaceOrder(o OrderRequest) error { order, err := newOrderFromRequest(o) if err != nil { // don't handle the error here, its handling may differ return err } // ... db transaction may also return an error return r.db.Save(order) }
在我們的 http package
中:
package http http.HandleFunc("/order", func (w http.ResponseWriter, r *http.Request) { orderRequest := createOrderRequest(r) err := orderRepo.PlaceOrder(orderRequest) if errors.Is(err, order.UnableToShipToCountry) { w.WriteHeader(http.StatusBadRequest) return } if err != nil { // this error in case of DB transaction failure w.WriteHeader(http.StatusInternalServerError) return } // ... w.WriteHeader(http.StatusOK) })
定制你的錯誤
我們可以創建我們自己的自定義錯誤值,并在我們的程序中使用它,同時考慮添加一些有用的信息,如錯誤跟蹤這可能會給我們的日志增加一個有益的價值,特別是在調試期間。
type AppErr struct { msg string code int trace string } func (e AppErr) Error() string { return fmt.Sprintf("Msg: %s, code: %d, trace:\n %s", e.msg, e.code, e.trace) } func NewAppErr(msg string, code int) AppErr { stackSlice := make([]byte, 512) s := runtime.Stack(stackSlice, false) return AppErr{msg, code, fmt.Sprintf("\n%s", stackSlice[0:s])} }
而我們在一個包內有這樣一個用例:
package admin func A() error { return b() } func b() error { return NewAppErr("error from b function!", 3) }
main.go:
func main() { err := admin.A() fmt.Println(err) }
記錄的錯誤信息將是:
Msg: error from b function!, code: 3, trace: ?
goroutine 1 [running]: ?
./cmd/app/error.NewAppErr({0x1f42b0, 0x17}, 0x7) ?
./cmd/app/error/error.go:16 +0x35 ?
./cmd/app/admin.b(...) ?
./cmd/app/admin/**admin.go:12** ?
./cmd/app/admin.A(...) ?
./cmd/app/admin/**admin.go:8** ?
main.main() ?
./cmd/app/**main.go:10** +0x8d
你也可以考慮在生產環境中關閉你的跟蹤打印,或者通過檢查其他配置值。
原文鏈接:https://juejin.cn/post/7166807817499148302
相關推薦
- 2022-04-10 C#實現簡單的計算器小功能_C#教程
- 2023-02-17 R語言繪制雙坐標圖的案例詳解_R語言
- 2022-07-08 Python基礎篇之字符串的最全常用操作方法匯總_python
- 2022-05-15 C++中類的轉換函數你了解嗎_C 語言
- 2023-09-18 springboot異常處理的一點總結
- 2022-09-25 Stream流水線的實現原理是什么
- 2022-04-24 redis用list做消息隊列的實現示例_Redis
- 2022-06-25 iOS使用UICollectionView實現拖拽移動單元格_IOS
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支