异步请求多个响应golang

异步请求多个响应golang

问题描述:

I need to make a request to a server that return different responses at different times. I mean, the server generate different responses and these responses take different execution time, so server return the responses as soon as they are available.

And I want print in the screen (by the moment, I'd settle with that) these responses as soon as the server returns me.

All what I could do until now is print the responses but only when the server returns all the responses. So if the first response take 1sec, and the last response take 10sec, my code needs to wait 10sec to print all the messages.

EDIT: to add code I have:

//Config is gotten from yml file
RestConfig       = Config["rest"].(map[string]interface{})
ServerConfig     = Config["server"].(map[string]interface{})
RequestUrl      := ServerConfig["url"]

RequestReader   := bytes.NewReader(body)
Request, _      := http.NewRequest("POST", RequestUrl.(string), RequestReader)

//AppendHeaders append the needing headers to the request 
client.AppendHeaders(Request, RestConfig["headers"])

//the type of client.HttpClient is *http.Client
Response, _     := client.HttpClient.Do(Request)

//And to print in the screen
defer Response.Body.Close()

fmt.Println( "-> Receiving response:
---
" )
fmt.Println( Response , "
---
-> Response body:
---
")
body_resp, _ := ioutil.ReadAll(Response.Body)
fmt.Println( string(body_resp) )
fmt.Println( "
--
")

Any way to do it??

Thank you very much.

Finally my code , is like this:

package main

import (
    "fmt"
    "log"
    "bytes"
    "strings"
    "bufio"
    "net/http"
)

func main() {
  var body = "The body"
  RequestReader := bytes.NewReader([]byte(body))
  req, err := http.NewRequest("POST", "the_url", RequestReader)
  if err != nil {
    log.Fatal(err)
  }
  req.Header.Add("Accept", "application/xml")
  req.Header.Add("Content-Type", "application/xml")
  req.Header.Add("AG-Authorization", "key")
  req.Header.Add("AG-Forwarded-Hosts", "*")

  resp, err := (&http.Client{}).Do(req)
  if err != nil {
    log.Fatal(err)
  }
  reader := bufio.NewReader(resp.Body)
  message := ""
  for {
    line, err := reader.ReadBytes('
')
    if err != nil {
      log.Fatal(err)
 }
    message = message + string(line)
    if strings.Contains(message, "<!-- End mark for each message -->"){
        fmt.Println(message)
        message = ""
    }
  }
}

Thank everyone.

The context package is what you are looking for.

The context package is responsible for signal cancelation and operation deadlines for processes and server requests. This has two public methods: WithCancel and WithTimeout. The Context associated with an incoming request is typically canceled when the request handler returns.

For your specific case you can use the WithTimeout method for setting a deadline on requests to backend servers.

// WithTimeout returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed, cancel is called, or timeout elapses. The new
// Context's Deadline is the sooner of now+timeout and the parent's deadline, if
// any. If the timer is still running, the cancel function releases its
// resources.
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

And here is a snippet taken from https://blog.golang.org/context/server/server.go

timeout, err := time.ParseDuration(req.FormValue("timeout")) // set a time limit in your post
if err == nil {
    // The request has a timeout, so create a context that is
    // canceled automatically when the timeout expires.
    ctx, cancel = context.WithTimeout(context.Background(), timeout)
} else {
    ctx, cancel = context.WithCancel(context.Background())
}
defer cancel() // Cancel ctx as soon as handleSearch returns.

For further reading take a look at this article: https://blog.golang.org/context