我正在開發一個帶有控制器、服務、儲存庫等的分層後端 API。
這些層中的每個方法都採用 context.Context
作為其包含請求上下文的第一個參數。這很方便,因為任何方法都可以存取各種與請求相關的資料(例如correlationID等)
每個請求上下文都有一個由下面的 TimeoutMiddleware
設定的超時:
func TimeoutMiddleware(timeoutFn func(*gin.Context) time.Duration) gin.HandlerFunc { return func(c *gin.Context) { timeout := timeoutFn(c) ctx, cancel := context.WithTimeout(c.Request.Context(), timeout) defer cancel() c.Request = c.Request.WithContext(ctx) c.Next() } } func TimeoutFn(c *gin.Context) time.Duration { return conf.HTTPRouter.DefaultContextTimeout }
這個想法是在請求上下文超時時優雅地停止任何正在進行的操作。 根據我對上下文和並發性的理解(非常少),我建立了這個輔助函數:
package helpers import "context" // Checks for context cancellation and returns ctx.Err() if canceled. func HandleContextCancel(ctx context.Context) error { select { case <-ctx.Done(): // If the context is canceled return ctx.Err() // return immediately with the canceled error. default: return nil // Continue with the normal processing. } }
理論上,如果我想盡快停止任何操作,我需要在應用程式中每個方法的開頭呼叫此函數,如下所示:
func DoSomething(ctx context.Context, ...) resterrors.RestErr { if err := helpers.HandleContextCancel(ctx); err != nil { return resterrors.NewRequestTimeoutError( fmt.Errorf("DoSomething: %w", err), ) } // ...
同時,我知道在存取資料庫的儲存庫中,大多數函數都需要上下文,例如Query
、QueryRow
、Exec
...,如下圖:
rows, err := pgclient.GetSession().Query(ctx, query, param1, ...)
因此,每次在上面的行中出現錯誤時,我必須檢查錯誤是否不是由於上下文取消所致,而不是僅返回 internal_server_error
和錯誤訊息,如下所示:
rows, err := pgclient.GetSession().Query(ctx, query, param1, ...) if err != nil { return helpers.MapRepoError("DoSomething: Query Error:", err) }
func MapRepoError(location string, err error) resterrors.RestErr { if errors.Is(err, context.DeadlineExceeded) { return resterrors.NewRequestTimeoutError( fmt.Errorf("%s request_timeout", location), ) } return resterrors.NewInternalServerError( fmt.Errorf("%s %w", location, err), ) }
使用 HandleContextCancel
函數看起來有點多餘,您對此有何看法?
您不需要 HandleContextCancel
函數,您可以簡單地執行以下操作:
if ctx.Err()!=nil { // Context timed out or canceled. Return return ctx.Err() }
如果您的其他錯誤處理函數正確地包裝了此錯誤(即它們實現了Unwrap() error
方法,那麼您可以在頂層檢查錯誤是否包含超時/取消錯誤,並決定您要處理什麼類型的錯誤喜歡返回。你不必每一層都這樣做。
以上是處理跨層上下文取消的詳細內容。更多資訊請關注PHP中文網其他相關文章!