亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

golang 資源所有權模式(文件、連接、關閉)

golang 資源所有權模式(文件、連接、關閉)

Go
不負相思意 2023-08-07 14:56:25
在 golang 中管理資源所有權的正確方法是什么?假設我有以下內容:db, err := sql.Open("mysql", "role@/test_db")am := NewResourceManager(db)am.DoWork()db.Close()總是讓調用函數維護關閉資源的所有權和責任是典型的嗎?這對我來說有點奇怪,因為關閉后,仍然保留引用,如果我或其他人稍后不小心,am可以嘗試使用(我想這是延遲的情況;但是,如果我想將 ResourceManager 傳回)從這個塊開始,我如何正確推遲文件的關閉?我實際上希望它在這個塊完成執行時保持打開狀態)。我發現在其他語言中,我經常希望允許實例管理資源,然后在調用析構函數時清理它,就像這個玩具 python 示例:dbamclass Writer():   def __init__(self, filename):       self.f = open(filename, 'w+')   def __del__(self):       self.f.close()   def write(value):       self.f.write(value)不幸的是,golang 中沒有析構函數。除了這樣的事情之外,我不確定如何在 go 中做到這一點:type ResourceManager interface {   DoWork()   // Close() ?}type resourceManager struct {  db *sql.DB}func NewResourceManager(db *sql.DB) ResourceManager {  return &resourceManager{db}} db, err := sql.Open("mysql", "role@/test_db")am := NewResourceManager(db)am.DoWork()am.Close()  // using method shortening但這似乎不太透明,而且我不確定如何傳達 ResourceManager 現在也需要 Close() 的信息。我發現這是一個常見的絆腳石,即我還想要一個保存 gRPC 客戶端連接的資源管理器,如果這些類型的資源不是由資源管理對象管理的,那么我的主要功能似乎是充斥著大量的資源管理,即打開和關閉。例如,我可以想象一種情況,我不想main知道有關該對象及其資源的任何信息:...func NewResourceManager() ResourceManager {  db, err := sql.Open("mysql", "role@/test_db")  return &resourceManager{db}}...// main elsewheream := NewResourceManager()am.DoWork()
查看完整描述

1 回答

?
溫溫醬

TA貢獻1752條經驗 獲得超4個贊

您選擇了一個不好的示例,因為您通常會重用數據庫連接,而不是每次使用時打開和關閉一個數據庫連接。因此,您可以將數據庫連接傳遞給使用它的函數,并在調用者中進行資源管理,而不需要資源管理器:


// Imports etc omitted for the sake of readability


func PingHandler(db *sql.DB) http.Handler (

? ? return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

? ? ? ?if err := db.ping(); err != nil {

? ? ? ? ? http.Error(w,e.Error(),500)

? ? ? ?}

? ? })

)


func main(){

? ? db,_ := sql.Open("superdb",os.Getenv("APP_DBURL"))


? ? // Note the db connection will only be closed if main exits.

? ? defer db.Close()


? ? // Setup the server

? ? http.Handle("/ping", PingHandler(db))

? ? server := &http.Server{Addr: ":8080"}


? ? // Create a channel for listening on SIGINT, -TERM and -QUIT

? ? stop := make(chan os.Signal, 1)


? ? // Register channel to be notified on said signals

? ? signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)


? ? go func(){

? ? ? ? ? ? // When we get the signal...

? ? ? ? ? ? <- stop

? ? ? ? ? ? ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)

? ? ? ? ? ? // ... we gracefully shut down the server.

? ? ? ? ? ? // That ensures that no new connections, which potentially

? ? ? ? ? ? // would use our db connection, are accepted.

? ? ? ? ? ? if err := server.Shutdown(ctx); err != nil {

? ? ? ? ? ? ? ? // handle err

? ? ? ? ? ? }

? ? }


? ? // This blocks until the server is shut down.

? ? // AFTER it is shut down, main exits, the deferred calls are executed.

? ? // In this case, the database connection is closed.

? ? // And it is closed only after the last handler call which uses the connection is finished.

? ? // Mission accomplished.

? ? server.ListenAndServe()

}

因此,在這個示例中,不需要資源管理器,而且老實說,我想不出真正需要資源管理器的示例。在極少數情況下,我需要類似的東西,我使用了sync.Pool.

但是,對于 gRPC 客戶端連接,也無需維護池:

[...]但是,ClientConn 應該自行管理連接,因此如果連接斷開,它將自動重新連接。如果您有多個后端,則可以連接到其中多個后端并在它們之間實現負載平衡。[...]

因此,同樣的原則適用:創建一個連接(池),根據需要傳遞它,確保在完成所有工作后關閉它。

不要將資源管理隱藏在其他地方,而是盡可能靠近使用資源的代碼并盡可能明確地管理資源。


查看完整回答
反對 回復 2023-08-07
  • 1 回答
  • 0 關注
  • 141 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號