Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Настройка контекста в Go
Контекст (context.Context) в Go — это инструмент для управления временем жизни операций, передачи данных между цепочкой вызовов и обработки отмены. Настройка контекста — ключевой навык для создания устойчивых и отзывчивых приложений.
Основные способы создания и настройки контекста
1. Создание базового контекста
// Пустой контекст без дедлайна и отмены
ctx := context.Background()
// Контекст для операций, где может потребоваться отмена (например, в main)
ctx := context.TODO()
Background() используется как корневой контекст, TODO() — как заглушка, когда неясно, какой контекст нужен.
2. Контекст с отменой
// Создаем контекст с функцией отмены
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Важно вызывать cancel для освобождения ресурсов
// В горутине проверяем отмену
go func(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Операция отменена:", ctx.Err())
return
default:
// Рабочая логика
}
}(ctx)
// Отменяем операцию где-то в другом месте
cancel()
3. Контекст с дедлайном
// Абсолютный дедлайн (конкретное время)
deadline := time.Now().Add(2 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// Относительный таймаут (более частый случай)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// Проверка дедлайна
if deadline, ok := ctx.Deadline(); ok {
fmt.Printf("Дедлайн: %v\n", deadline)
}
4. Контекст с передачей данных
// Добавляем пары ключ-значение
type contextKey string
const requestIDKey contextKey = "requestID"
ctx := context.WithValue(context.Background(), requestIDKey, "req-123")
ctx = context.WithValue(ctx, "userID", 456)
// Извлекаем значения
if reqID, ok := ctx.Value(requestIDKey).(string); ok {
fmt.Printf("Request ID: %s\n", reqID)
}
Важно: используйте пользовательские типы для ключей, чтобы избежать коллизий.
Практические паттерны настройки
Каскадная отмена
func processChain(ctx context.Context) error {
// Создаем дочерний контекст с более строгим таймаутом
childCtx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
// Передаем контекст дальше
return downstreamOperation(childCtx)
}
Отмена родительского контекста автоматически отменяет все дочерние.
Контекст для HTTP-запросов
// На стороне клиента
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
// На стороне сервера (обработчик)
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
select {
case result := <-longOperation(ctx):
fmt.Fprintf(w, "Result: %v", result)
case <-ctx.Done():
http.Error(w, "Request cancelled", http.StatusRequestTimeout)
}
}
Рекомендации по настройке
-
Всегда передавайте контекст явно как первый аргумент функций, которые выполняют I/O или блокирующие операции.
-
Вызывайте cancel() через defer сразу после создания контекста с отменой, если не требуется более сложное управление жизненным циклом:
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
- Правильно обрабатывайте отмену в циклах и долгих операциях:
for {
select {
case <-ctx.Done():
return ctx.Err() // Возвращаем причину отмены
case data := <-ch:
process(data)
}
}
-
Используйте контекст для сквозных данных (tracing ID, аутентификация), но не для передачи параметров приложения.
-
Логируйте отмены для отладки:
select {
case <-ctx.Done():
log.Printf("Operation cancelled: %v, reason: %v", operationName, ctx.Err())
return
}
Типичные ошибки
- Хранение контекста в структурах: контекст должен передаваться между вызовами, но не храниться в полях структур (кроме случаев, когда структура представляет собой сам запрос, как
http.Request). - Игнорирование возвращаемого cancel(): невызов cancel() может привести к утечке памяти.
- Использование глобальных контекстов: каждый запрос/операция должен иметь свой контекст.
Правильная настройка контекста делает приложения более устойчивыми к сбоям, позволяет контролировать ресурсы и обеспечивает лучшую пользовательскую опыт за счет отзывчивости системы.