Ответ
Создание дочерних контекстов в Go — это ключевой механизм для управления жизненным циклом операций и передачи данных в рамках одного запроса. Основные цели и преимущества:
Пропаганда отмены (Cancellation Propagation)
- Преимущество: Это главная возможность контекста. Если родительский контекст отменяется (например, пользователь закрыл вкладку браузера, и HTTP-запрос прервался), все дочерние контексты также автоматически отменяются. Это позволяет грациозно остановить всю цепочку операций (например, запросы в базу данных, вызовы других сервисов), связанных с исходным запросом, и освободить ресурсы.
- Создание:
context.WithCancel(parentCtx)
Управление дедлайнами и таймаутами (Deadlines and Timeouts)
- Преимущество: Можно задать дочернему контексту более строгий (но не более мягкий) дедлайн, чем у родителя. Это полезно, чтобы ограничить время выполнения конкретной подзадачи. Например, на весь HTTP-запрос у нас есть 2 секунды, а на конкретный вызов в базу данных мы хотим выделить не более 500 мс.
- Создание:
context.WithTimeout(parentCtx, duration)
илиcontext.WithDeadline(parentCtx, time)
Передача Request-Scoped данных (Passing Values)
- Преимущество: Позволяет передавать данные, относящиеся ко всему запросу (ID трассировки, информация о пользователе), вниз по стеку вызовов, не загромождая сигнатуры функций. Важно помнить, что этот механизм следует использовать только для данных запроса, а не для передачи опциональных параметров в функции.
- Создание:
context.WithValue(parentCtx, key, value)
Ключевое правило: Отмена или истечение времени жизни дочернего контекста никогда не влияет на его родителя. Это обеспечивает изоляцию и предсказуемость.
Пример использования WithTimeout
:
func main() {
// Создаем родительский контекст с таймаутом в 2 секунды
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // Важно вызывать cancel, чтобы освободить ресурсы
// Запускаем долгую операцию в горутине
go longOperation(ctx)
// Ждем сигнала отмены от контекста
<-ctx.Done()
fmt.Println("Основная функция: операция отменена по таймауту или завершена.")
time.Sleep(100 * time.Millisecond) // Даем время горутине вывести сообщение
}
func longOperation(ctx context.Context) {
select {
case <-time.After(3 * time.Second): // Имитация работы, которая длится 3 секунды
fmt.Println("Операция успешно завершена")
case <-ctx.Done(): // Контекст будет отменен через 2 секунды
fmt.Println("Операция прервана сигналом отмены:", ctx.Err())
}
}