3
\$\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\$
1
\$\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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.