2015-08-16 14 views
5

Ho un codice (vedi sotto) scritto in Go, che si suppone che "fan-out" le richieste HTTP e raccolga/aggreghi i dettagli indietro.Come gestire gli errori di timeout HTTP e l'accesso ai codici di stato in golang

I'm new to golang and so expect me to be a nOOb and my knowledge to be limited

L'output del programma è attualmente qualcosa come:

{ 
    "Status":"success", 
    "Components":[ 
     {"Id":"foo","Status":200,"Body":"..."}, 
     {"Id":"bar","Status":200,"Body":"..."}, 
     {"Id":"baz","Status":404,"Body":"..."}, 
     ... 
    ] 
} 

C'è un server locale in esecuzione che è volutamente lenta (posti letto per 5 secondi e poi restituisce una risposta). Ma ho altri siti elencati (vedi il codice qui sotto) che a volte attivano anche un errore (se si sbaglia, allora va bene).

Il problema che ho al momento è il modo migliore per gestire questi errori, e in particolare gli errori relativi al "timeout"; nel senso che non sono sicuro di come riconoscere se un errore è un timeout o qualche altro errore?

Al momento ottengo un errore di coperta indietro per tutto il tempo: (! Si spera per timeout)

Get http://localhost:8080/pugs: read tcp 127.0.0.1:8080: use of closed network connection 

Dove http://localhost:8080/pugs sarà generalmente l'url che non è riuscita. Ma come puoi vedere dal codice (sotto), non sono sicuro di come determinare il codice di errore sia correlato a un timeout né come accedere al codice di stato della risposta (attualmente sono solo blanket impostandolo su 404 ma ovviamente non è giusto - se il server dovesse commettere errori mi aspetterei qualcosa di simile a un codice di stato 500 e ovviamente mi piacerebbe riflettere che nella risposta aggregata io rispedisco indietro).

Il codice completo può essere visualizzato di seguito. Qualsiasi aiuto apprezzato.

package main 

    import (
      "encoding/json" 
      "fmt" 
      "io/ioutil" 
      "net/http" 
      "sync" 
      "time" 
    ) 

    type Component struct { 
      Id string `json:"id"` 
      Url string `json:"url"` 
    } 

    type ComponentsList struct { 
      Components []Component `json:"components"` 
    } 

    type ComponentResponse struct { 
      Id  string 
      Status int 
      Body string 
    } 

    type Result struct { 
      Status  string 
      Components []ComponentResponse 
    } 

    var overallStatus string = "success" 

    func main() { 
      var cr []ComponentResponse 
      var c ComponentsList 

      b := []byte(`{"components":[{"id":"local","url":"http://localhost:8080/pugs"},{"id":"google","url":"http://google.com/"},{"id":"integralist","url":"http://integralist.co.uk/"},{"id":"sloooow","url":"http://stevesouders.com/cuzillion/?c0=hj1hfff30_5_f&t=1439194716962"}]}`) 

      json.Unmarshal(b, &c) 

      var wg sync.WaitGroup 

      timeout := time.Duration(1 * time.Second) 
      client := http.Client{ 
        Timeout: timeout, 
      } 

      for i, v := range c.Components { 
        wg.Add(1) 

        go func(i int, v Component) { 
          defer wg.Done() 

          resp, err := client.Get(v.Url) 

          if err != nil { 
           fmt.Printf("Problem getting the response: %s\n", err) 

           cr = append(cr, ComponentResponse{ 
            v.Id, 
            404, 
            err.Error(), 
           }) 
          } else { 
            defer resp.Body.Close() 
            contents, err := ioutil.ReadAll(resp.Body) 
            if err != nil { 
              fmt.Printf("Problem reading the body: %s\n", err) 
            } 

            cr = append(cr, ComponentResponse{ 
              v.Id, 
              resp.StatusCode, 
              string(contents), 
            }) 
          } 
        }(i, v) 
      } 
      wg.Wait() 

      j, err := json.Marshal(Result{overallStatus, cr}) 
      if err != nil { 
        fmt.Printf("Problem converting to JSON: %s\n", err) 
        return 
      } 

      fmt.Println(string(j)) 
    } 
+0

Molto probabilmente estranei a il tuo problema, ma hai una corsa di dati che si aggiunge a 'cr'. Non è possibile scrivere la stessa variabile da più goroutine senza sincronizzazione. Potresti voler costruire/correre con il [rivelatore di gare] (https://blog.golang.org/race-detector). –

+0

Grazie per il commento. Cercherò invece di utilizzare i canali +. Indagherò quel rilevatore di gare :-) – Integralist

+1

Se la chiamata del cliente restituisce un errore, non c'è alcun codice di stato, perché non c'era una richiesta http completa. Non c'è molto che puoi fare con un errore a quel punto, ma in go1.5 un client. Il timeout restituirà almeno un messaggio migliore in un errore net.Error. – JimB

risposta

-1

Il rilascio Go 1.5 risolto questo problema essendo più specifiche sul tipo di errore ha gestito.

Quindi, se vedete questo esempio https://github.com/Integralist/Go-Requester/blob/master/requester.go#L38 vedrai che io sono in grado di applicare un modello di espressione regolare per il messaggio di errore di decifrare se l'errore è stato davvero un timeout o non

status := checkError(err.Error()) 

func checkError(msg string) int { 
    timeout, _ := regexp.MatchString("Timeout", msg) 

    if timeout { 
     return 408 
    } 

    return 500 
} 
+1

Non gironzolare per le stringhe di errore * meglio * è incredibilmente fragile; la stringa di errore è * non * parte di qualsiasi API. L'interfaccia ['net.Error'] (https://golang.org/pkg/net/#Error) definisce il metodo' Timeout' per questo. –

+0

@Dave C grazie per il commento ma non sono sicuro di come funzioni ciò che hai suggerito. L'errore che torno da client.Get è una stringa. Non ha metodi. Quindi niente da interrogare – Integralist

+0

Non è solo una stringa, è un 'errore'. Tutto ciò che non è contenuto nella stringa di risultati formattata, chiamando la funzione di controllo con 'err.Error()'. Invece si dovrebbe tentare un tipo di asserzione e altri controlli. Solo se tutto ciò non funziona dovresti a) inviare una segnalazione di bug su un errore non verificabile eb) come un brutto rimedio (con un grosso commento di avvertenza) * forse * grep in giro nella stringa (o cambiare il codice che stai chiamando per restituire un errore verificabile e inviarlo come richiesta di modifica a monte). –

1

Se si vuole ventaglio poi fuori i risultati aggregati e si desidera che il comportamento di timeout specifica il pacchetto net/http non ti dà, quindi si consiglia di utilizzare goroutines e canali.

Ho appena visto questo video oggi e vi guiderà attraverso esattamente quegli scenari utilizzando le funzionalità di concorrenza di Go. Inoltre, l'oratore Rob Pike è piuttosto autorevole - lo spiega molto meglio di quanto avrei potuto.

https://www.youtube.com/watch?v=f6kdp27TYZs

+0

il downvote è per la pubblicazione di un video di un'ora. dov'è la risposta? – circuitry

Problemi correlati