We’ve covered how to create website in chapter 1,
and how to deploy to GAE in chapter 2,
This chapter describes how to optimise cost of the blog created.
Saving Cost
Basic principle follows the one described in this article.
It is recommended to limit the spending unless if you want to maintain 24/7 uptime of server to prevent spontaneous hike in billing.
Your app.yaml should also contain elements to control instance so you don’t use excessive amount of resources:
automatic_scaling:
max_instances: 1
max_idle_instances: 1
min_pending_latency: "3000ms"
Another important strategy to incorporate is cache control.
Cache for static content such as css/js/img/fonts can be defined easily with expiration
and default_expiration
element:
default_expiration: "1h"
handlers:
- url: /css
static_dir: public/css
expiration: "7d"
- url: /js
static_dir: public/js
expiration: "7d"
- url: /img
static_dir: public/img
expiration: "7d"
- url: /fonts
static_dir: public/fonts
expiration: "7d"
The expiration
and default_expiration
is only effective to static
or static_dir
element.
We are not able to attach expiration
element to script
handler.
- url: /.*
script: _go_app
This means, we have to define cache control ourselves in main.go
.
This website teaches well in how to add HTTP caching in GO.
Putting it into practice, I came up with below main.go
package main
import (
"os"
"net/http"
"strings"
)
func init() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
etag := `"` + "<some etag string>" + `"`
w.Header().Set("Etag", etag)
w.Header().Set("Cache-Control", "max-age=3600")
if match := r.Header.Get("If-None-Match"); match != "" {
if strings.Contains(match, etag) {
w.WriteHeader(http.StatusNotModified)
return
}
}
if _, err := os.Stat("./public/" + r.URL.Path); os.IsNotExist(err) {
http.ServeFile(w, r, "./public/404.html")
} else {
http.ServeFile(w, r, "./public/" + r.URL.Path)
}
})
}
When using above main.go
in GAE, we have to make sure the etag string (<some etag string>) gets updated whenever serving new version otherwise user will constantly re-use their locally stored cache file.
For convenience, I have created a script file to output new main.go which updates eTag string before deploying to GAE.
Further to add, noticed this line?
if _, err := os.Stat("./public/" + r.URL.Path); os.IsNotExist(err)
By having this check, you can provide user with your own custom 404 page for missing path.
That’s it!