Idiomatic way of handling http requests in separate goroutines

252 views
Skip to first unread message

Pablo Rozas Larraondo

unread,
Dec 7, 2016, 7:01:18 PM12/7/16
to golang-nuts
Hi gophers,

I'm designing a service exposed through a http server which uses a pool of workers to process requests. At the moment my handlerFunc look similar to this:

func aHandler(w http.ResponseWriter, r *http.Request) {
        var wg sync.WaitGroup
        wg.Add(1)
        reqQueue <- &RequestParams{&wg, r, w}
        wg.Wait()
}

When a worker is done, it writes on the w and calls wg.Done(). This approach works fine but I'm not sure if passing the WaitGroup around is the best approach. Has anyone encountered a similar situation or can suggest alternative approaches? Could the new context package and its Done() channel be of any help in this case? 

Thank you very much for your help,
Pablo

Tamás Gulácsi

unread,
Dec 8, 2016, 9:33:14 AM12/8/16
to golang-nuts
I think this is not bad.
But you can try context.Context, or simply pass in a new chan, which will be closed by the worker - so you can select, timeout on it.

Pablo Rozas-Larraondo

unread,
Dec 8, 2016, 5:06:36 PM12/8/16
to golang-nuts
Thank you Tamás, 

I like the concept of blocking on select in the handler until a worker closes the channel. I don't know why but every time I use a WaitGroup I have the impression that the same thing can be done better.

I'll try to implement it with the context library first as I'm very intrigued about it.

Cheers,
Pablo

Pablo Rozas-Larraondo

unread,
Dec 13, 2016, 12:49:16 AM12/13/16
to golang-nuts
In case someone arrives here with the same question I'm going to post a simple example that does what Tamás suggested a few days ago. Use context to block on .Done() and pass the responseWriter as a context Value. Please comment if passing the responseWriter as a context value is not considered a good practice.

package main

import (
"context"
"net/http"
"time"
)

var reqChan chan context.Context

func ConcPrinter(reqChan chan context.Context) {
for c := range reqChan {
w := c.Value("reqW").(http.ResponseWriter)
f := c.Value("cancel").(context.CancelFunc)
// Expensive operation
time.Sleep(time.Second)
w.Write([]byte("Done!"))
f()
}
}

func rootHandler(w http.ResponseWriter, r *http.Request) {
cont, f := context.WithCancel(context.WithValue(r.Context(), "reqW", w))
contC := context.WithValue(cont, "cancel", f)
reqChan <- contC
<-contC.Done()
}

func main() {
http.HandleFunc("/", rootHandler)
reqChan = make(chan context.Context)
go ConcPrinter(reqChan)
http.ListenAndServe(":8080", nil)
}

Cheers,
Pablo
Reply all
Reply to author
Forward
0 new messages