Как использовать context.WithCancel и context.WithTimeout API вместе и нужно ли это? - PullRequest
0 голосов
/ 13 июня 2018

Теперь я сделал что-то вроде этого:

func contextHandler(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx, cancel := context.WithCancel(r.Context())
        ctx, cancel = context.WithTimeout(ctx, config.EnvConfig.RequestTimeout)

        defer cancel()

        if cn, ok := w.(http.CloseNotifier); ok {
            go func(done <-chan struct{}, closed <-chan bool) {
                select {
                case <-done:
                case <-closed:
                    logger.Debug("message", "client connection has gone away, request will be cancelled")
                    cancel()
                }
            }(ctx.Done(), cn.CloseNotify())
        }

        h.ServeHTTP(w, r.WithContext(ctx))
    })
}

Просьба обратить внимание на эти две строки:

ctx, cancel := context.WithCancel(r.Context())
ctx, cancel = context.WithTimeout(ctx, config.EnvConfig.RequestTimeout)

Согласно моим тестам: deliberately kill the client request и deliberately make the request exceed the deadline, обаработают нормально (я имею в виду, что могут получить сигнал отмены и сигнал тайм-аута, как и ожидалось), просто моя проблема: последняя функция cancel переопределит предыдущую, возвращаемую context.WithCancel(r.Context()), поэтому:

  1. Это правильный способ использовать эти два API вместе, как это?
  2. Есть ли необходимость использовать эти два API вместе?

Пожалуйста, помогите объяснить.

Ответы [ 2 ]

0 голосов
/ 16 октября 2018

Вы можете просто использовать WithTimeout() вместо использования обоих API, потому что WithTimeout() возвращает вам context.CancelFunc, как и WithCancel(), который может быть вызван в любое время для отмены целевого процесса / процедуры.Конечно, отмена должна до наступления срока, установленного WithTimeout().

Итак,

Это правильный способ использовать эти два API вместе, как это?
Есть ли необходимость использовать эти два API вместе?

Нет, не нужно использовать оба, используйте возвращенный context.CancelFunc любым API в контексте пакета.

0 голосов
/ 13 июня 2018

Поскольку CancelFunc, возвращаемый из вашего вызова WithCancel, немедленно перезаписывается, это вызывает утечку ресурсов (т. Е. Памяти) в вашей программе.Из контекстной документации :

Функции WithCancel, WithDeadline и WithTimeout берут Context (родительский) и возвращают производный Context (дочерний) и CancelFunc.Вызов CancelFunc отменяет дочерний элемент и его дочерние элементы, удаляет ссылку родителя на дочерний элемент и останавливает все связанные таймеры. Если не вызвать функцию CancelFunc, утечка дочернего элемента и его дочерних элементов происходит до тех пор, пока родитель не будет отменен или не сработает таймер.

Удаление контекста WithCancel из вашего кода решит эту проблему.

Кроме того, HTTP-сервер управляет отменой HTTP-запроса, как описано в документации по методу http.Request.Context:

Для входящих запросов к серверу:контекст отменяется, когда клиентское соединение закрывается, запрос отменяется (с HTTP / 2) или когда возвращается метод ServeHTTP.

Когда сервер отменяет контекст запроса, все дочерние контексты будутотменен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...