Время ожидания Goroutine

type Response struct {
  data   interface{}
  status bool
}

func Find() (interface{}, bool) {
  ch := make(chan Response, 1)

  go func() {
    data, status := findCicCode()
    ch <- Response{data: data, status: status}
  }()

  select {
  case response := <-ch:
    return response.data, response.status
  case <-time.After(50 * time.Millisecond):
    return "Request timed out", false
  }
}

Итак, у меня есть вышеуказанная функция. В принципе findCicCode() вызов функции делает 3 http-вызова внутренне внешним службам. Я добавил комбинированное время ожидания для этих трех вызовов HTTP. Не могу указать индивидуальное время ожидания в моем случае. Но он все еще делает вызовы API в фоновом режиме, если он превышает время ожидания.

Я не уверен, что здесь есть утечка горутина. Есть ли способ отменить эти запросы https, если есть тайм-аут?

2 ответа

Решение

Вы контролируете отмену http запросов с помощью context.Context,

// create a timeout or cancelation context to suit your requirements
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

req, err := http.NewRequest("GET", location, nil)

// add the context to each request and they will be canceled in unison
resp, err := http.Do(req.WithContext(ctx))

Если вы хотите, вы можете создать свою собственную систему тайм-аута для произвольной работы, выполнив одну операцию приема на канале (в основной процедуре), и в зависимости от того, какая другая программа достигнет своей операции отправки первой - time.Sleep или тот, кто делает реальную работу - побеждает.

Вот полный работающий пример / симуляция. Настройте значения времени ожидания и задержки для имитации различных сценариев. Канал не буферизируется и закрывается после считывания одного значения, чтобы позволить другой программе завершиться при отправке.

package main

import(
    "fmt"
    "time"
)

type Response struct {
    Data        []byte
    Status      int
}

func Wait(s int) {
    time.Sleep(time.Duration(s) * time.Second)
}

func FindWrapper(ch chan Response, delay int) {
    // Put real find stuff here...

    // Dummy response after wait for testing purposes
    Wait(delay)
    ch <- Response{[]byte("Some data..."), 200}
}

func main() {
    timeout := 3
    delay := 4
    ch := make(chan Response)

    // whoever sends to ch first wins...
    go func() {
        Wait(timeout)
        ch <- Response{}
    }()
    go FindWrapper(ch, delay)

    r := <-ch
    close(ch)
    if r.Data == nil {
        r.Status = 500 // or whatever you want for timeout status
    }
    fmt.Printf("Data: %s  Status: %d\n", string(r.Data), r.Status)
}

Буферный канал тоже будет работать. И вы могли бы сделать то же самое с sync.WaitGroup когда вы вызываете Add только один раз, а затем закрываете канал после wg.Wait(),

Тем не менее, я предлагаю попробовать решение JimB использования Context время ожидания, так как оно, вероятно, работает для вашего варианта использования и является менее сложным решением.

Другие вопросы по тегам