We have been using Node.js rather successfully on the DeltaX Tracking and Ad-serving side of things with it churning >10K requests/min without breaking a sweat. The inherent asynchronous event-driven nature of Node.js helps us keep the latencies low and the memory footprint small. 1

In recent times, Go (aka. golang) has come close to challenging Node.js with regards to building light-weight high-performance micro-services and also brings own set of advantages to the table. 2

Two of the primary advantages according to me are:

  • Raw Performance: Go has performance characteristics very close to C and C++ while Node.js being dynamically typed cannot reach the raw performance of Go for CPU and Memory bound tasks.

  • Explicit Concurrency: Go introduced goroutines. A goroutine is a function that is capable of running concurrently with other functions in its own lightweight thread. Communication with other goroutines is handled elegantly using channels.3

Re-writing /status and /clicks handler in Go

I try to keep myself away from programming language wars but at the same time, I also feel that the best way to evaluate a language is by trying to do something you know with single-minded focus. This is exactly why I thought of porting the /status and /clicks handler from DeltaX tracking code to Go. Here is a small snippet (and it doesn’t cover all the use cases currently handled by the tracking system).

// Initialize
// --------------------
func main() {
  http.HandleFunc("/", indexHandler)
  http.HandleFunc("/status", indexHandler)
  http.HandleFunc("/clicks", clicksHandler)

  // Serving root-based resources
  //serveSingle("/sitemap.xml", "./public/sitemap.xml")
  //serveSingle("/robots.txt", "./public/robots.txt")
  serveSingle("/favicon.ico", "./public/favicon.ico")

  // Serve static resources from /public directory
  http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./public/"))))

  err := http.ListenAndServe(":8080", nil)
  if err != nil {
    log.Fatal("ListenAndServe error:", err)
  }
}

// Clicks handler
// --------------------
func clicksHandler(w http.ResponseWriter, r *http.Request) {
  uid, _ := r.Cookie("_id")
  uuid, _ := newUUID()
  if uid != nil {
    expiration := time.Now().Add(365 * 24 * time.Hour)
    cookie := http.Cookie{Name: "_id",Value:uuid,Expires:expiration}      
    http.SetCookie(w, &cookie)
  } 

  http.Redirect(w, r, r.FormValue("xu"), 301)
}

Note: The full source code is available in the Bitbucket project dxtrackerGo.

Closing thoughts
  • When it comes to ease of adapting to the language - for anyone coming from a Javascript or any dynamically typed programming language background to Node.js would feel at home. Go should be easier for someone who comes more from a professional C or C++ background.
  • For anything that’s CPU and memory bound Node.js isn’t a viable option. Also, if you want to use Node.js in a more synchronous fashion doing series of related lookups you would either have to go down the path of callback-hell (yes, this is a term) or find libraries which help you sequence callbacks without having to stall the execution of the single threaded Node.js execution.
  • For network event drive application the simplicity and performance of Node.js is on part with Go and other best of the breed languages.
  • The Node.js ecosystem is fairly rich and with the recent change in its governing structure the development of Node.js itself has caught pace. On the other hand, Google putting its might and resources behind a language and you are rest assured that the future seems bright for Go .
Resources