@vgrabkowot

Как правильно работать с Context?

Добрый день. У меня есть функция:
func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p := getProviders(w, r)
	defer p.Close()

	ctx, cancel := context.WithTimeout(context.Background(), config.Read().HTTP.Timeout.Request*time.Second)
	defer cancel()
	h, isExist := nodePathGet(router.getBaseNode(r.Method), r.RequestURI)

	if !isExist {
		router.Errors.PageNotFound(ctx, p)
	} else {
		go func() {
			router.middlewareHandler(h)(ctx, p)
			cancel()
		}()
	}

	<-ctx.Done()
	if ctx.Err() == nil {
		if ctx.Err() == context.DeadlineExceeded {
			log.Println(r.Method, r.RequestURI, "error request timeout")
			router.Errors.RequestTimeout(ctx, p)
		}
	}
}

Проблема в том что когда функция router.middlewareHandler(h)(ctx, p) работает дольше положенного таймаута то срабатывает defer p.Close() который освобождает память, а потом рутина пытается туда что-то записать и все падает с паникой.

Я хочу реализовать стандартный таймаут. Если функция не завершается дольше определённого времени то мы ее "убиваем" и выводим ошибку. Как это правильно реализовать?
  • Вопрос задан
  • 356 просмотров
Решения вопроса 1
Во-первых, лучше контекст все же брать не Background, а из http-запроса. Таким образом вы будете корректно обрабатывать ситуацию, когда соединение с клиентом разорвалось и не надо больше обрабатывать запрос (контекст отменится).
ctx, cancel := context.WithTimeout(r.Context(), config.Read().HTTP.Timeout.Request*time.Second)


Во-вторых, контекст обычно не используют для ожидания внутренних горутин, он не для этого предназначен. Для этого используют sync.WaitGroup.
Поэтому у вас и возникает проблема, ибо событие отмены контекста еще не значит, что дочерние функции мгновенно завершились. Возникает ситуация, когда чтение канала <-ctx.Done() внутри приведенной вами функции происходит раньше, чем такое же чтение канала внутри функции router.middlewareHandler(h). Поэтому нужно ждать не отмены контекста, а завершения работы горутины.

В-третьих, вам тут вообще не нужна горутина, ибо ожидание ее завершения вы сделали сразу после нее. Вместо этого можно просто написать последовательный код.

В-четвертых, вы, наверное, хотели написать if ctx.Err() != nil {. Но у вас вместо этого ==, что вообще обесценивает код внутри этого ифа.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы