I am writing some Go web services (also implementing the webserver in Go with http.ListenAndServe). I have a map of structs which I would like to keep in memory (with an approximate data size of 100Kb) to be used by different HTTP requests.
How can this be achieved in Go? I am thinking to use global package variables or caching systems (like memcache/groupcache).
The POST response body can only be cached for subsequent GET requests to the same resource. Set the Location or Content-Location header in the POST response to communicate which resource the body represents. So the only technically valid way to cache a POST request, is for subsequent GETs to the same resource.
Expiration is the caching mechanism by which a client can entirely avoid making requests to the origin server. When the origin server specifies an explicit expiration time in the resource, a cache can check that expiration time and respond accordingly without having to contact the server first.
Myth #7 – HTTPS Never Caches People often claim that HTTPS content is never cached by the browser; perhaps because that seems like a sensible idea in terms of security. In reality, HTTPS caching is controllable with response headers just like HTTP.
In a web farm the Session can be local (which works only if affinity is set), or remote (state server or database, or custom), but the cache is always local. So, storing a DataTable in the cache will consume memory, but it will not use serialization.
In addition to the answers you've already received, consider making use of receiver-curried method values and http.HandlerFunc.
If your data is data that is loaded before the process starts, you could go with something like this:
type Common struct {
Data map[string]*Data
}
func NewCommon() (*Common, error) {
// load data
return c, err
}
func (c *Common) Root(w http.ResponseWriter, r *http.Request) {
// handler
}
func (c *Common) Page(w http.ResponseWriter, r *http.Request) {
// handler
}
func main() {
common, err := NewCommon()
if err != nil { ... }
http.HandleFunc("/", common.Root)
http.HandleFunc("/page", common.Page)
http.ListenAndServe(...)
}
This works nicely if all of the Common
data is read-only. If the Common
data is read/write, then you'll want to have something more like:
type Common struct {
lock sync.RWMutex
data map[string]Data // Data should probably not have any reference fields
}
func (c *Common) Get(key string) (*Data, bool) {
c.lock.RLock()
defer c.lock.RUnlock()
d, ok := c.data[key]
return &d, ok
}
func (c *Common) Set(key string, d *Data) {
c.lock.Lock()
defer c.lock.Unlock()
c.data[key] = *d
}
The rest is basically the same, except instead of accessing the data through the receiver's fields directly, you'd access them through the getters and setters. In a webserver where most of the data is being read, you will probably want an RWMutex, so that reads can be executed concurrently with one another. Another advantage of the second approach is that you've encapsulated the data, so you can add in transparent writes to and/or reads from a memcache or a groupcache or something of that nature in the future if your application grows such a need.
One thing that I really like about defining my handlers as methods on an object is that it makes it much easier to unit test them: you can easily define a table driven test that includes the values you want and the output you expect without having to muck around with global variables.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With