Уроки модуля (5/5)
`context.Context`: отмена и сроки
Пакет context является стандартом де-факто для распространения
отмена, сроки и ограниченные значения по цепочке вызовов,
в нескольких горутинах. Все стандартные «сетевые» пакеты
(net/http, database/sql, os/exec) примите context.Context как
их первый параметр.
Интерфейс
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}Done(): канал, который закрывается при отмене контекста.Err(): причина отмены (context.Canceledилиcontext.DeadlineExceeded).Deadline(): необязательный крайний срок.Value(): поиск значений, привязанных к области (используйте экономно).
Четыре основные функции
ctx := context.Background() // radice: usalo in main, init, test
ctx := context.TODO() // "non so ancora": stub esplicito
ctx, cancel := context.WithCancel(parent) // cancel manuale
ctx, cancel := context.WithTimeout(parent, d) // scadenza relativa
ctx, cancel := context.WithDeadline(parent, t) // scadenza assoluta
ctx := context.WithValue(parent, key, value) // valore scope-boundВсе производные формы возвращают функцию cancel: всегда вызывайте ее.
(в идеале с defer), чтобы освободить внутренние ресурсы, даже если
контекст уже истек.
Шаблон тайм-аута
func fetchUser(id string) (*User, error) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
return queryUser(ctx, id) // queryUser deve rispettare ctx
}Если queryUser занимает более 2 секунд, ctx.Done() закрывается и
функция должна прервать операцию.
Соблюдая контекст: выберите Done().
func slowWork(ctx context.Context) error {
for {
select {
case <-ctx.Done():
return ctx.Err() // canceled o deadline exceeded
case <-time.After(100 * time.Millisecond):
// un tick di lavoro
}
}
}Правило: везде, где ваша функция может заблокироваться на длительное время, она должен обрабатывать ctx.Done(). В противном случае контекст «включен» на ничего.
Соглашение о первом параметре
func Fetch(ctx context.Context, url string) ([]byte, error) { ... }
// ^^^^^^^^^^^^^^^^^^^
// SEMPRE primo, mai dentro a una struct, mai opzionalectxВСЕГДА в качестве первого параметра с именемctx.- Никогда не помещайте
Contextв качестве поля структуры (очень редкие исключения). - Никогда не передавайте
nil: используйтеcontext.TODO()в качестве заглушки.
context.WithValue: экономно
type userKey struct{}
ctx := context.WithValue(ctx, userKey{}, currentUser)
// più sotto:
if u, ok := ctx.Value(userKey{}).(*User); ok { ... }Рекомендации:
- ТОЛЬКО для данных области запроса (идентификатор запроса, идентификатор трассировки, идентификатор пользователя).
- НИКОГДА для параметров функции: передавайте их как обычно.
- Ключи типа пользовательский неэкспортируемый, чтобы избежать коллизий.
Распространение по цепочке
func handleHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // context legato al ciclo di vita della richiesta
user, err := fetchUser(ctx, r.URL.Query().Get("id"))
if err != nil { ... }
// ...
}net/http автоматически отменяет r.Context(), когда клиент
отключается или когда обработчик возвращается. Распространяя это на всех
нисходящие вызовы, все дерево операций отменяется в
каскад: нет напрасной работы после отключения.
Попробуйте это
Создайте контекст с таймаутом в 1 секунду, полученным из context.Background(), и не забудьте вызвать команду cancel с помощью defer.
Показать подсказку
`context.WithTimeout(parent, d)` возвращает `(ctx, cancel)`; всегда звоните, отменяйте.
Решение доступно после 3 попыток
Используйте <-ctx.Done() при выборе, чтобы дождаться отмены контекста.
Показать подсказку
`ctx.Done()` — это канал, который закрывается при отмене контекста.
Решение доступно после 3 попыток
По соглашению, где находится параметр ctx в сигнатуре функции?
func Fetch(???, url string) ([]byte, error)Резюме
context.Contextраспространяет значения отмены, крайнего срока и области действия.Background()/TODO()в качестве корня;WithCancel/WithTimeout/WithDeadlineдля получения.- Всегда
defer cancel()для освобождения ресурсов. - «Длинные» функции должны
selectвctx.Done()или передавать ctx в стандартные пакеты. – Соглашение:ctxв качестве ПЕРВОГО параметра, никогда внутри структуры. WithValueтолько для данных области запроса (идентификатор запроса, идентификатор трассировки).- В
net/httpr.Context()отменяется при отключении клиента.