Tommy

Tommy

写代码是热爱,但也是生活 !
github

[Golang] context引發的一個小bug

先看代碼:

func AsyncAdd(run func() error)  {
	//TODO: 扔進異步協程池
	go run()
}

func GetInstance(ctx context.Context,id uint64) (string, error) {
	data,err := GetFromRedis(ctx,id)
	if err != nil && err != redis.Nil{
		return "", err
	}
	// 沒有找到數據
	if err == redis.Nil {
		data,err = GetFromDB(ctx,id)
		if err != nil{
			return "", err
		}
		AsyncAdd(func() error{
			return UpdateCache(ctx,id,data)
		})
	}
	return data,nil
}

func GetFromRedis(ctx context.Context,id uint64) (string,error) {
	// TODO: 從redis獲取信息
	return "",nil
}

func GetFromDB(ctx context.Context,id uint64) (string,error) {
	// TODO: 從DB中獲取信息
	return "",nil
}

func UpdateCache(ctx context.Context,id interface{},data string) error {
	// TODO:更新緩存信息
	return nil
}

func main()  {
	ctx,cancel := context.WithTimeout(context.Background(), 3 * time.Second)
	defer cancel()
	_,err := GetInstance(ctx,2021)
	if err != nil{
		return
	}
}

原因:因為誤用 導致異步更新失敗

分析#

我們先簡單分析一下,這一段代碼要幹什麼?其實很簡單,我們想要獲取一段信息,首先會從緩存中獲取,如果緩存中獲取不到,我們就從 DB 中獲取,從 DB 中獲取到信息後,在協程池中放入更新緩存的方法,異步去更新緩存。整個設計是不是很完美,但是在實際工作中,異步更新緩存就沒有成功過。。。。

導致失敗的原因就在這一段代碼:

AsyncAdd(func() error{
			return UpdateCache(ctx,id,data)
		})

錯誤的原因只有一個,就是這個 ctx,如果改成這樣,就啥事沒有了。

AsyncAdd(func() error{
			ctxAsync,cancel := context.WithTimeout(context.Background(),3 * time.Second)
			defer cancel()
			return UpdateCache(ctxAsync,id,data)
		})

原因#

在原始的代碼中,UpdateCache 函數被調用時使用的是外部的上下文對象 ctx,這樣會導致 UpdateCache 函數的超時時間受到了 GetInstance 函數傳入的上下文對象的影響。

通過在匿名函數中創建一個新的上下文對象 ctxAsync 並將其傳遞給 UpdateCache 函數,可以確保 UpdateCache 函數的超時時間獨立於外部的上下文對象。

同時,由於 ctxAsync 是在匿名函數內部創建的,所以需要在函數執行結束時調用 cancel () 函數來釋放相關資源。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。