4
\$\begingroup\$

I am a golang novice... I am trying to write an application that takes in many requests - up to a sustained 10000 HTTP posts/sec and post the payload to two back-ends in parallel (5 second timeout). A canned message is returned to the client, it doesn't matter whether the backed end returns a response or errors out, the same response text is sent to the client.

I have a first crack at it below. I'm not worried about error handling yet. My thinking is that I can make each back-end call asynchronous using goroutines and let the go runtime deal with queuing/threads for the go routines. I simply need to perform the post and log the result, there is no communication between goroutines. I have had others guide me to using channels, but given that I don't need to return any data back from the post I'm not sure I need them.

Here is my first very simple crack at it. Please make any suggestions on the code and approach!

package main

import (
    "fmt"
    "github.com/go-chi/chi"
    "net/http"
    "io/ioutil"
    "bytes"
    "time"
)

func main() {
    r := chi.NewRouter()
    r.Post("/", func(w http.ResponseWriter, r *http.Request) {
        handlePost(w, r);
    })

    http.ListenAndServe(":3000", r)

}

func handlePost(w http.ResponseWriter, r *http.Request) {
    body, _ := ioutil.ReadAll(r.Body)
    fmt.Fprintf(w, "dummy response will go here");
    go callBackend("http://backend1", body)
    go callBackend("http://backend2", body)
}

func callBackend(url string, body []byte) {

    req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/xml")

    client := &http.Client{
        Timeout: time.Second * 5,
    }
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Sprintf("backend [%s] response status: %s", url, resp.Status)
    fmt.Println("backend response Headers:", resp.Header)
    responsebody, _ := ioutil.ReadAll(resp.Body)
    fmt.Println("backend response Body:", string(responsebody))

}
\$\endgroup\$
2
\$\begingroup\$
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
    handlePost(w, r)
})

can be simplified to

r.Post("/", handlePost)

since both functions have same signature.


fmt.Fprintf(w, "dummy response will go here")

No need to use printf family functions where you don't need formatting. You can write directly with

w.Write([]byte("dummy response will go here"))

or with

io.WriteString(w, "dummy response will go here")

responsebody, _ := ioutil.ReadAll(resp.Body)
fmt.Println("backend response Body:", string(responsebody))

This one reads the whole response into memory for no reason. Use io.Copy to write it more efficiently:

fmt.Print("backend response Body: ")
_, err := io.Copy(os.Stdout, resp.Body)

I'm not worried about error handling yet.

Then everything else is totally fine.

\$\endgroup\$
0

This site is temporarily in read only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .