(утечка goroutine) http.TimeoutHandler не убивает соответствующий goroutine ServeHTTP - PullRequest
0 голосов
/ 26 июня 2019

Обработчик тайм-аута перемещает выполнение ServeHTTP для новой процедуры, но не может уничтожить эту процедуру после окончания таймера.При каждом запросе создается две подпрограммы, но подпрограммы ServeHTTP никогда не уничтожаются с помощью контекста.

Не удается найти способ уничтожения подпрограмм.

Редактировать Цикл For сФункция time.Sleep, представляет огромные вычисления, которые выходят за рамки нашего таймера.Может заменить его любой другой функцией.

package main

import (
    "fmt"
    "io"
    "net/http"
    "runtime"
    "time"
)

type api struct{}

func (a api) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    // For-loop block represents huge computation and usually takes more time
    // Can replace with any code
    i := 0
    for {
        if i == 500 {
            break
        }
        fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
        time.Sleep(1 * time.Second)
        i++
    }
    _, _ = io.WriteString(w, "Hello World!")
}

func main() {
    var a api
    s := http.NewServeMux()
    s.Handle("/", a)
    h := http.TimeoutHandler(s, 1*time.Second, `Timeout`)

    fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())

    _ = http.ListenAndServe(":8080", h)
}

Программа ServeHTTP должна завершать работу вместе с контекстом запроса, обычно это не происходит.

Ответы [ 2 ]

1 голос
/ 26 июня 2019

Используйте context.Context , чтобы дать указания подпрограммам прервать свою функцию.Конечно, рутины должны прослушивать такие события отмены.

Так что для своего кода сделайте что-то вроде:

ctx := req.Context() // this will be implicitly canceled by your TimeoutHandler after 1s

i := 0
for {
    if i == 500 {
        break
    }

    // for any long wait (1s etc.) always check the state of your context
    select {
    case <-time.After(1 * time.Second): // no cancelation, so keep going
    case <-ctx.Done():
        fmt.Println("request context has been canceled:", ctx.Err())
        return // terminates go-routine
    }
    i++
}

Playground: https://play.golang.org/p/VEnW0vsItXm


Примечание: Context предназначены для объединения в цепочку, что позволяет каскадным образом отменять несколько уровней подзадач.

При обычном вызове REST можноинициировать запрос к базе данных.Таким образом, чтобы гарантировать, что такая блокировка и / или медленный вызов завершаются своевременно, вместо использования Query необходимо использовать QueryContext - передачу в качестве запроса http контекста в качестве первого аргумента.

0 голосов
/ 25 июля 2019

Я обнаружил, что если у вас нет никакого способа добраться до вашего канала, то нет способа убить или остановить рутину, когда он работает.

В большой вычислительной задаче вы должны наблюдатьканал на определенный интервал или после завершения конкретной задачи.

...