Ответ
Что такое Context Propagation?
Context Propagation (проброс или распространение контекста) — это паттерн передачи информации о запросе, его состоянии и сигналах управления (отмена, таймаут) по цепочке вызовов функций, особенно в асинхронных и распределенных системах.
В Go этот механизм реализован через стандартный пакет context
и интерфейс context.Context
.
Зачем он нужен?
Контекст решает три основные задачи:
- Отмена операций (Cancellation): Позволяет грациозно остановить цепочку горутин, если родительская операция была отменена (например, пользователь закрыл вкладку браузера).
- Контроль времени выполнения (Deadlines/Timeouts): Гарантирует, что операция не будет выполняться дольше заданного времени.
- Передача данных в рамках запроса (Request-scoped data): Позволяет передавать метаданные (ID запроса, токен аутентификации) вниз по стеку вызовов, не загрязняя сигнатуры функций.
Как это работает в Go?
context.Background()
: Пустой корневой контекст, который используется как отправная точка для всех контекстов.context.WithCancel(parent)
: Создает дочерний контекст, который можно отменить, вызвав возвращаемую функциюcancel()
.context.WithTimeout(parent, duration)
: Создает дочерний контекст, который автоматически отменяется по истечении таймаута.context.WithValue(parent, key, value)
: Добавляет в контекст данные типа ключ-значение.
Пример:
// Создаем контекст с таймаутом в 2 секунды
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // Важно вызывать cancel, чтобы освободить ресурсы
// Передаем контекст в долгую операцию
result, err := longOperation(ctx)
if err != nil {
// Ошибка может быть вызвана таймаутом
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("Операция отменена по таймауту")
}
}
func longOperation(ctx context.Context) (string, error) {
select {
case <-time.After(3 * time.Second): // Имитация долгой работы
return "Результат", nil
case <-ctx.Done(): // Канал Done() закрывается при отмене или таймауте
return "", ctx.Err() // Возвращаем ошибку контекста
}
}
Ключевые принципы:
- Иммутабельность: Контекст неизменяем. Функции
WithCancel
,WithTimeout
иWithValue
возвращают новый, дочерний контекст. - Явная передача: Контекст всегда передается как первый аргумент функции:
func DoSomething(ctx context.Context, ...)
. - Не для бизнес-логики: Не используйте
context.WithValue
для передачи обязательных параметров. Он предназначен для сквозных, request-scoped данных.