I'm trying to build a little website in Go containing a report based on data collected from a web service. It uses an API to query the service for data. The service can only be queried once every few seconds.
However, I have to query it a number of times to get the complete report data. Right now I just hammer the API to update my whole data structure each time the http handler (http.HandleFunc) is called. Of course, this is bad because it triggers lots of queries to the external API that are throttled. So, my report comes up very, very, very, slowly.
What I want to do instead is to have a function to updateReportData in a non-blocking way and store that data in some variable that the http.HandleFunc() can just ingest without calling the external API.
But, I'm very new to Go (and things like closures, semaphores, concurrency, etc) and so I'm not really sure how to build this. Should I be using channels? Should I use timers? How can I get the updateReportData to not block the http.HandleFunc, but still run on a fixed interval?
To sum up, I want to have a background routine update a data structure on a fixed interval and I want to be able to use http.HandleFunc to serve whatever data is in the data structure any time i make an http request to the program. I just have no idea how to start. Any advice would be appreciated.
There are a few things you need to do:
Here is a simplified example showing how to use a goroutine and a sync.RWMutext
to accomplish what you want:
package main
import (
"fmt"
"net/http"
"sync"
"time"
)
var (
timeSumsMu sync.RWMutex
timeSums int64
)
func main() {
// Start the goroutine that will sum the current time
// once per second.
go runDataLoop()
// Create a handler that will read-lock the mutext and
// write the summed time to the client
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
timeSumsMu.RLock()
defer timeSumsMu.RUnlock()
fmt.Fprint(w, timeSums)
})
http.ListenAndServe(":8080", nil)
}
func runDataLoop() {
for {
// Within an infinite loop, lock the mutex and
// increment our value, then sleep for 1 second until
// the next time we need to get a value.
timeSumsMu.Lock()
timeSums += time.Now().Unix()
timeSumsMu.Unlock()
time.Sleep(1 * time.Second)
}
}