At Lincoln Loop, we have been building large web applications using Django since 2007. Recently, however, we have recently started using Go as a critical network component of Botbot.me.
Reading others’ successes stories about replacing some existing components of their infrastructure with a new incarnation written in Go (Disqus and Iron.io gave me the motivation to build a webapp that is a bit more complex than the canonical “hello world” one page app.
The result of this experiment is called gowebexp
and is available on BitBucket. Here are a few things I learned in the process.
Go is a modern language with an amazingly deep standard libraries given its age. It comes with everything you need to build a webapp:
- template language
- full featured webserver
- HTTP request multiplexer
- cookies
- …
However it is a totally different beast than something like “Django”:htpp://djangoproject.com/. Go standard libraries tend to be very low level and, as of now, I am not aware of any full-featured framework than can compare with Django in terms of ease of use, features, and documentation.
For example, here’s a few things that you take for granted when you use Django that need to be handled manually:
- form validation
- Cross site request forgery (CSRF) protection
- …
When building a webapp that goes beyond a single page, you’ll quickly notice the difference and very quickly find yourself in a situation where you really miss your familiar tools. In the Go ecosystem Gorilla is a popular choice for building on the web. It is a collection of packages that you can assemble as your need grows. Gowebexp
imports 4 packages from the Gorilla toolkit:
github.com/gorilla/context
github.com/gorilla/mux
github.com/gorilla/securecookie
github.com/gorilla/sessions
The names are mostly self explanatory. github.com/gorilla/mux
implements a request router and dispatcher. From its documentation:
The name mux stands for “HTTP request multiplexer”. Like the standard http.ServeMux, mux.Router matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions.
mux
probably overkill for my simple experiment but I wanted to work with tool that I feel confident to use for something bigger.
Getting started with gowebexp
The README provides a step-by-step procedure on how to install gowebexp
.
Coming from the Python land this is an area where Go really shines. The tooling around the language is fantastic and built-in. Go comes with a command to get a package and all its dependencies named go get
similar to pip install
.
Installing and downloading all the dependencies is as simple as:
go get bitbucket.org/yml/gowebexp
You can install and compile it using:
go install bitbucket.org/yml/gowebexp
Since we are talking about tooling, Go also comes with a test runner:
go test bitbucket.org/yml/gowebexp/...
There are several others that I encourage you to check out:
go fmt ...
go vet ...
go doc ...
Anatomy of gowebexp
It is built around 3 components in 2 packages:
- A command to start the web server
- A page
package that describes the object we are managing in
gowebexp
:Page
- a
web
package that contains the logic requires to deliver aPage
gowebexp
uses a singleton pattern to create an App
instance that stores all the information that we pass around.
This is very useful to provide information inside the function handlers because they only take two arguments http.ResponseWriter
and http.Request
. These functions build the response that is returned to the browser. Quickly after the joy of responding “hello world” you will feel the need to have more things available.
I found that I needed a way to access my router, storages, templates or cookie store. This App
singleton is initialized when the web
package is imported so it is always available. It is based on the following struct
that can be extended further:
Sample view: PageList
The terminology I’m using here should be familiar to Django users. In a Model, View, Template paradigm, the View is responsible for collecting all the information that will be rendered in a Template. The example below displays the list of pages and creates a new one:
The overall control flow is very similar to what you find in a Django functional view, but there are two things that might require some explanation:
ctx
is amap[string]interface{}
. Go, being a statically typed language, requires a bit of ceremony to build a map of anything. The closest thing available in Go is an empty interfaceinterface{}
.ctx
lets us push anything in the template.- The second interesting thing here is
page.Validate()
. This method returns amap[string]string
that contains the validation errors. Unlike Django’sModelForm
you on your own to implement the exact logic you want to support.
A word about performance
I know people either love or hate micro-benchmarks, so I will be very short on this and point you to this series of benchmarks. It compares languages and frameworks in a careful and repeatable way.
In a nutshell Go is FAST. Below is a result from an Apache bench run this will give you an idea of the order of magnitude we are talking about.
Conclusion
Writing a simple webapp in Go is much more work than Django or Ruby on Rails developers have grown accustomed to. The tools available in the standard libraries are very low level. Happily you will find packages in the ecosystem to limit the number of wheels you have to reinvent.
On one hand, the best practices to write a large webapp in go are not as formalized as are in the Django world. Looking through Github, BitBucket and co, I noticed the coding styles and patterns are still very diverse and not always for the best. I have probably done a ton of things that could be improved in gowebexp
.
On the other hand Go is already a fantastic tool that you can use to ease growing and scaling pain points. Its footprint is so small and it is abile to serve dynamic pages with massive throughput . In addition to this, the deployment story is ridiculously simple. All you need to do is copy a statically linked binary and your templates/static folders. Its simple deployment and fantastic performance will make Go an important tool for scaling large websites in the future.