Context — это Golang пакет включающий структуру Context
и вспомогательные функции.
Основная идея этого пакета — контролировать время выполнения сетевых запросов (http, tcp and etc.)
С помощью него можно установить таймаут для запроса
context.WithTimeout
(функция является оберткой над context.WithDeadline
)
или отменить по условию
context.WithCancel()
Отмена запроса по таймауту
- Пример из жизни. Вы звоните другу и ждёте пока он ответит. Обычно вы ждёте какое-то время, за которое он должен ответить на звонок, например 20 секунд. Если он так и не взял трубку, вы отменяете вызов.
- Пример реальной задачи. Скрипт ежедневно забирает данные по API из внешнего сервиса. Задача — завершить запрос к API, если он не выполнился в течении 20 секунд.
В следующем примере также используется функция context.Background()
. Она возвращает пустой контекст (обычно он используется для инициализации контекстов).
Пример кода:
// создается контекст с таймаутом, первый аргумент функции родительский контекст.
ctx, cancel := context.WithTimeout(context.Background(), 20 * time.Second)
defer cancel()
// создается заспрос с использованием контекста.
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "<api_url>", nil)
if err != nil {
return nil, fmt.Errorf("failed to create request with ctx: %w", err)
}
// отправка запроса.
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to perform http request: %w", err)
}
return res, nil
Отмена запроса по условию
- Пример из жизни. Вы школьник и не можете решить задачу по математике. Идёте к родителям и сестре за помощью. Мама, папа и сестра начали решать задачку, сестра решила первая. В итоге вы говорите родителям, что задачу больше решать не надо, сестра уже решила.
- Пример реальной задачи. Скрипт ищет сервер, который ответит быстрее всех. Идёт отправка запросов на 10 серверов. Когда получили первый ответ, отменяем все остальные запросы.
Пример кода:
var servers = []string{"<SERVER_URL_1>", "<SERVER_URL_2>", "<SERVER_URL_3>"}
var wg sync.WaitGroup
// создается контекст. Аргумент родительский контекст.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// цикл по серверам, среди которых ищем самый быстрый.
for _, url := range servers {
wg.Add(1)
go func() {
// создается заспрос и использованием контекста.
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
fmt.Errorf("failed to create request with ctx: %w", err)
}
// Отправка запроса.
_, err = http.DefaultClient.Do(req)
if err != nil {
fmt.Errorf("failed to perform http request: %w", err)
}
wg.Done()
// Ответ получен отменяем контекст. Все остальные запросы отменятся в этот момент.
cancel()
}()
}
wg.Wait()
.....
go func() {
for {
select {
case <-ctx.Done():
// Здесь код исполнится, когда самый быстрый сервер найден.
return
}
}
}()
Видео о контексте в Golang
Оставить комментарий