Skip to content

Instantly share code, notes, and snippets.

@erikh

erikh/client.go Secret

Created May 8, 2013 01:50
Show Gist options
  • Save erikh/fab4b9ef990664b1b56d to your computer and use it in GitHub Desktop.
Save erikh/fab4b9ef990664b1b56d to your computer and use it in GitHub Desktop.
package main
import "log"
import "net/http"
import "time"
import "io/ioutil"
func main() {
tick := time.Tick(100 * time.Microsecond)
for {
select {
case <-tick:
res, err := http.Get("http://localhost:8081")
if err != nil {
log.Println(err.Error())
continue
}
defer res.Body.Close()
b, _ := ioutil.ReadAll(res.Body)
log.Println(string(b))
}
}
}
package main
import "encoding/json"
import "net/http"
import "io/ioutil"
import "time"
import "fmt"
import "log"
import "sync"
type JSONResult struct {
Result bool
}
const TickTime = 10
const CacheExpiration = 30
var Cache = make(map [string] JSONResult)
var CacheTime = make(map [string] time.Time)
var CacheLock = make(map [string] *sync.Once)
var ChanMap = make(map [string] chan JSONResult)
type MyHandler struct {}
func update_cache() {
for {
for k, v := range ChanMap {
select {
case result := <-v:
Cache[k] = result
CacheTime[k] = time.Now()
CacheLock[k] = new(sync.Once)
}
}
time.Sleep(1)
}
}
func hit(url string) (JSONResult, error) {
var json_res JSONResult
log.Println("http://localhost:8080" + url)
res, err := http.Get("http://localhost:8080" + url)
if err != nil {
log.Println(err.Error())
return json_res, err
}
b, _ := ioutil.ReadAll(res.Body)
log.Println(string(b))
err = json.Unmarshal(b, &json_res)
if err != nil {
log.Println(err.Error())
return json_res, err
}
result, ok := Cache[url]
log.Println(result, json_res, ok)
if ok && result != json_res {
ChanMap[url] <- json_res
} else if !ok {
ChanMap[url] <- json_res
}
return json_res, nil
}
func check(url string, quit <- chan time.Time) {
tick := time.Tick(TickTime * time.Second)
for {
select {
case <-quit:
return
case <-tick:
hit(url)
}
}
}
func getfirstresult(url string) []byte {
var b []byte
log.Println("Getting first result")
_, ok := ChanMap[url]
if !ok {
ChanMap[url] = make(chan JSONResult)
}
res, err := hit(url)
if err != nil {
return nil
}
b, _ = json.Marshal(res)
_, ok = CacheLock[url]
if !ok {
CacheLock[url] = new(sync.Once)
}
go CacheLock[url].Do(func() { check(url, time.After(CacheExpiration * time.Second)) })
return b
}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var b []byte
url := r.URL.Path
result, ok := Cache[url]
w.Header().Add("Content-Type", "application/json")
if ok && (time.Now().Unix() - CacheTime[url].Unix()) < CacheExpiration {
b,_ = json.Marshal(result)
} else {
b = getfirstresult(url)
}
fmt.Fprintln(w, string(b))
log.Printf("handled request with %s\n", string(b))
}
func main() {
s := &http.Server{
Addr: ":8081",
Handler: &MyHandler{},
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 2048,
}
go update_cache()
s.ListenAndServe()
}

code review

Thanks for peeking -- so server.go is the actual API server -- all it does is spit out some json with a t/f and flips it each hit. proxy.go is the meat. I'll explain it below. client.go, like server.go is just something to exercise the proxy more or less.

proxy

the proxy takes a request the first time, and forwards it along to the server. it then caches the result for up to CacheExpiration seconds and only hits it every TickTime seconds. The point of this is to offload frequently hit but infrequently changing requests for API services, such as EC2's instance status API.

I plan on making this into a library once I get a firmer grip on Go.

review request

What I really want is a good solid look at my goroutine and channel usage -- the API and error handling in particular don't concern me as much right now, but the concurrency does, and what I can do to make it better.

package main
import "encoding/json"
import "net/http"
import "time"
import "fmt"
import "log"
type MyHandler struct {
Result bool
}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
b, _ := json.Marshal(h)
fmt.Fprintln(w, string(b))
log.Printf("handled request: %b", h.Result)
h.Result = !h.Result
}
func main() {
s := &http.Server{
Addr: ":8080",
Handler: &MyHandler{},
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 2048,
}
s.ListenAndServe()
}
@erikh
Copy link
Author

erikh commented May 8, 2013

fwiw, I'm aware the code is pretty ugly -- it's about the usage patterns right now for me. This is very much "beat on it until it works" code atm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment