I am learning GO and wanted to make a simple rest API.
What I want to do is fire off a goroutine after handling the api request and do the work asynchronously in the background.
Here's my implementation so far:
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
// APIResponse represents common structure of every api call response
type APIResponse struct {
Status string `json:"status"`
Error string `json:"error,omitempty"`
Data string `json:"data,omitempty"`
}
// Action represents what could be passed to the goroutine
type Action = func()
// ActionQueue represents a buffered channel
type ActionQueue = chan Action
func main() {
r := httprouter.New()
r.GET("/test", test)
var apiServerPort = ":80"
err := http.ListenAndServe(apiServerPort, r)
if err != nil {
log.Fatal("ListenAndServe:", err)
} else {
log.Printf("Started server on port %s", apiServerPort)
}
var queueSize = 10
queue := make(ActionQueue, queueSize)
for i := 0; i < queueSize; i++ {
go worker(queue)
}
log.Printf("Started %d queue workers", queueSize)
}
func test(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
successResponse(w, http.StatusOK, "Hello World")
queue <- func() {
log.Println("Hello from queue worker, initiated by api call")
}
}
func successResponse(w http.ResponseWriter, statusCode int, successData string) {
sendResponse(w, statusCode, "", successData)
}
func errorResponse(w http.ResponseWriter, statusCode int, errorData string) {
sendResponse(w, statusCode, errorData, "")
}
func sendResponse(w http.ResponseWriter, statusCode int, errorData string, responseData string) {
w.WriteHeader(statusCode)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(&APIResponse{Status: http.StatusText(statusCode), Error: errorData, Data: responseData})
}
func worker(queue ActionQueue) {
for action := range queue {
action()
}
}
When I try to run this code, I am getting the following error (on this line queue <- func() { ... }
):
./main.go:46:2: undefined: queue
How can I make the queue
channel available to my request handler (i.e. httprouter GET request handler func)?
Secondly, I cannot see the output from my log.Printf()
calls in the console output (stdout), e.g. server status message when the app runs. Any ideas why?
Couple of things are wrong, first your main function is not ordered correctly, you want to initialize you channel and start workers before you run err := http.ListenAndServe(apiServerPort, r)
so something like this
func main() {
var queueSize = 10
queue := make(ActionQueue, queueSize)
for i := 0; i < queueSize; i++ {
go worker(queue)
}
log.Printf("Started %d queue workers", queueSize)
// routers and stuff...
}
Then queue
variable is not defined in the test()
function, which is why you are getting ./main.go:46:2: undefined: queue
. You could fix it with a higher order function e.g.
func test(queue ActionQueue) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
successResponse(w, http.StatusOK, "Hello World")
queue <- func() {
log.Println("Hello from queue worker, initiated by api call")
}
}
}
and then just bind it to the route with r.GET("/test", test(queue))