Ответ
В стандартной библиотеке Go есть несколько ключевых функций для работы с контекстами:
context.Background()
: Создает пустой корневой контекст. Обычно используется вmain()
, инициализаторах и тестах как контекст верхнего уровня.context.TODO()
: Так же, как иBackground()
, создает пустой контекст. Используется как временная заглушка, когда неясно, какой контекст использовать, или когда функция еще не адаптирована для работы с контекстом.context.WithCancel(parent)
: Создает дочерний контекст и возвращает функциюcancel
. При вызовеcancel()
этот контекст и все его дочерние контексты отменяются.context.WithDeadline(parent, time)
: Создает контекст, который отменяется по наступлению определенного времени (time
).context.WithTimeout(parent, duration)
: Удобная обертка надWithDeadline
, которая отменяет контекст по истеченииduration
.
context.WithValue
context.WithValue
используется для передачи request-scoped данных, то есть данных, относящихся к конкретному запросу, например, ID запроса, токен аутентификации или информация для трассировки.
Правила и лучшие практики:
- Не для бизнес-логики: Никогда не передавайте через контекст обязательные параметры функции (например, ID пользователя для выборки из БД). Это делает зависимости неявными и усложняет тестирование и понимание кода. Параметры должны передаваться в функцию явно.
- Используйте кастомные типы ключей: Чтобы избежать коллизий ключей между разными пакетами, в качестве ключа следует использовать неэкспортируемый кастомный тип.
Пример правильного использования (передача ID запроса для логирования):
// Для ключа используется неэкспортируемый тип, чтобы избежать коллизий.
type key int
const requestIDKey key = 0
func main() {
// Предположим, это middleware, который добавляет ID к каждому запросу.
requestID := "abc-123-xyz-789"
ctx := context.WithValue(context.Background(), requestIDKey, requestID)
// Передаем контекст дальше по цепочке вызовов.
processRequest(ctx)
}
func processRequest(ctx context.Context) {
// Извлекаем ID запроса для логирования.
if reqID, ok := ctx.Value(requestIDKey).(string); ok {
log.Printf("[%s] Обработка запроса...", reqID)
} else {
log.Printf("Обработка запроса... (ID не найден)")
}
}