首頁  >  文章  >  後端開發  >  處理跨層上下文取消

處理跨層上下文取消

WBOY
WBOY轉載
2024-02-05 23:54:07476瀏覽

處理跨層上下文取消

問題內容

我正在開發一個帶有控制器、服務、儲存庫等的分層後端 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),
    )
}
// ...

同時,我知道在存取資料庫的儲存庫中,大多數函數都需要上下文,例如QueryQueryRowExec ...,如下圖:

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中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除