context
в Go — это специальный пакет, предоставляющий механизм для:
Основной интерфейс:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Базовый контекст, используемый как корневой:
ctx := context.Background()
Аналогичен Background(), но используется как временный заглушка:
ctx := context.TODO()
Создает контекст с возможностью отмены:
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Важно вызывать cancel для освобождения ресурсов
Контекст с автоматической отменой по таймауту:
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
Аналогичен WithTimeout, но принимает конкретное время:
ctx, cancel := context.WithDeadline(context.Background(), time.Date(2023, 12, 31, 23, 59, 59, 0, time.UTC))
defer cancel()
Контекст для передачи значений:
ctx := context.WithValue(context.Background(), "requestID", "12345")
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Передаем контекст в нижележащие вызовы
result, err := someLongOperation(ctx)
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
row := db.QueryRowContext(ctx, "SELECT ...")
GRPC построен вокруг context и автоматически передает его между вызовами.
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
return // завершаем работу при отмене
default:
// выполняем работу
}
}
}
Контекст должен быть первым параметром:
func DoSomething(ctx context.Context, arg1, arg2 string) error
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
// работа
}
}
Контекст должен передаваться между функциями, а не храниться:
// Плохо:
type Server struct {
ctx context.Context
}
// Хорошо:
func (s *Server) HandleRequest(ctx context.Context)
ctx = context.WithValue(ctx, "traceID", generateTraceID())
func BadPractice(arg string) { // Нет параметра ctx
// Код, который может блокироваться
}
// Плохо - использование строк как ключей может привести к коллизиям
ctx := context.WithValue(ctx, "userID", 123)
// Хорошо - использовать свой тип
type ctxKey string
const userIDKey ctxKey = "userID"
ctx := context.WithValue(ctx, userIDKey, 123)
go func() {
// Горутина будет висеть, даже если контекст отменен
}()
Правильное использование context делает Go-программы более надежными и предсказуемыми.