Program your next server in Go Sameer Ajmani Manager, Go team Google

Video This talk was presented at the ACM Applicative conference in New York City on June 1, 2016. Watch the talk on YouTube 2

Outline 1. What is Go, and who uses it?

2. Comparing Go and other languages

3. Code examples

4. Concurrency

5. Getting started 3

What is Go? "Go is an open source programming language that makes it easy to build simple, reliable, and efficient software." golang.org 4

History Design began in late 2007. Robert Griesemer, Rob Pike, and Ken Thompson.

Ian Lance Taylor and Russ Cox. Open source since 2009 with a very active community. Language stable as of Go 1, early 2012. Go 1.7 is coming this August. 5

Why Go? Go is an answer to problems of scale at Google. 6

System Scale designed to scale to 10⁶⁺ machines

everyday jobs run on 1000s of machines

jobs coordinate, interact with others in the system

lots going on at once Solution: great support for concurrency 7

A Second Problem: Engineering Scale In 2011: 5000+ developers across 40+ offices

20+ changes per minute

50% of code base changes every month

50 million test cases executed per day

single code tree Solution: design the language for large code bases 8

Who uses Go at Google? Hundreds of projects. Thousands of Go programmers. Millions of lines of Go code. Public examples: Flywheel: SPDY proxy for Chrome on mobile devices 9

Who uses Go at Google? Hundreds of projects. Thousands of Go programmers. Millions of lines of Go code. Public examples: Flywheel: SPDY proxy for Chrome on mobile devices

dl.google.com: Download server for Chrome, ChromeOS, Android SDK, Earth, etc.

Vitess: YouTube MySQL balancer

Seesaw: Linux Virtual Server (LVS) based load balancer

Lingo: Logs analysis in Go, migrated from Sawzall The target is networked servers, but Go is a great general-purpose language. 10

Who uses Go besides Google? golang.org/wiki/GoUsers Aerospike, BBC Worldwide, Bitbucket, Booking.com, Core OS, Datadog, Digital Ocean, Docker, Dropbox, Facebook, Getty Images, GitHub, GOV.UK, Heroku, IBM, Intel, InfluxDB, Iron.io, Kubernetes, Medium, MongoDB, Mozilla services, Netflix, New York Times, pool.ntp.org, Rackspace, Shutterfly, SmugMug, SoundCloud, SpaceX, Square, Stack Exchange, Thomson Reuters Eikon, Tumblr, Twitch, Twitter, Uber, VMWare ... 11

Comparing Go and other languages 12

"Go: 90% Perfect, 100% of the time" -bradfitz, 2014 13

Go has much in common with Java C family (imperative, braces)

Statically typed

Garbage collected

Memory safe (nil references, runtime bounds checks)

Variables are always initialized (zero/nil/false)

Methods

Interfaces

Type assertions ( instanceof )

) Reflection 14

Go differs from Java in several ways Fast, efficient for computers: Programs compile to machine code. There's no VM.

Control over memory layout, fewer indirections Fun, fast for humans: Simple, concise syntax

Statically linked binaries

Function values and lexical closures

Built-in strings (UTF-8)

Built-in generic maps and arrays/slices

Built-in concurrency 15

Go intentionally leaves out many features No classes

No inheritance

No constructors

No final

No exceptions

No annotations

No user-defined generics 16

Why does Go leave out those features? Clarity is critical. When reading code, it should be clear what the program will do. When writing code, it should be clear how to make the program do what you want. Sometimes this means writing out a loop instead of invoking an obscure function. (Don't DRY out.) For more background on design: Less is exponentially more (Pike, 2012)

Go at Google: Language Design in the Service of Software Engineering (Pike, 2012) 17

Code examples 18

Go looks familiar Hello, world! package main import "fmt" func main() { fmt.Println("Hello, 世界!") } 19

Hello, web server package main import ( "fmt" "log" "net/http" ) func main() { http.HandleFunc("/hello", handleHello) fmt.Println("serving on http://localhost:7777/hello") log.Fatal(http.ListenAndServe("localhost:7777", nil)) } func handleHello(w http.ResponseWriter, req *http.Request) { log.Println("serving", req.URL) fmt.Fprintln(w, "Hello, 世界!") } Types follow names in declarations.

Exported names are Capitalized. Unexported names are not. 20

Example: Google Search frontend // +build ignore,OMIT // The server program issues Google search requests. It serves on port 8080. // // The /search endpoint accepts these query params: // q=the Google search query // // For example, http://localhost:8080/search?q=golang serves the first // few Google search results for "golang". package main import ( "encoding/json" "fmt" "html/template" "log" "net/http" "time" "golang.org/x/talks/content/2016/applicative/google" ) func main() { http.HandleFunc("/search", handleSearch) fmt.Println("serving on http://localhost:8080/search") log.Fatal(http.ListenAndServe("localhost:8080", nil)) } // handleSearch handles URLs like "/search?q=golang" by running a // Google search for "golang" and writing the results as HTML to w. // The query parameter "output" selects alternate output formats: // "json" for JSON, "prettyjson" for human-readable JSON. func handleSearch(w http.ResponseWriter, req *http.Request) { log.Println("serving", req.URL) // Check the search query. query := req.FormValue("q") // HL if query == "" { http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest) return } // ENDQUERY OMIT // Run the Google search. start := time.Now() results, err := google.Search(query) // HL elapsed := time.Since(start) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // ENDSEARCH OMIT // Create the structured response. type response struct { Results []google.Result Elapsed time.Duration } resp := response{results, elapsed} // HL // ENDRESPONSE OMIT // Render the response. switch req.FormValue("output") { case "json": err = json.NewEncoder(w).Encode(resp) // HL case "prettyjson": var b []byte b, err = json.MarshalIndent(resp, "", " ") // HL if err == nil { _, err = w.Write(b) } default: // HTML err = responseTemplate.Execute(w, resp) // HL } // ENDRENDER OMIT if err != nil { log.Print(err) return } } var responseTemplate = template.Must(template.New("results").Parse(` <html> <head/> <body> <ol> {{range .Results}} <li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li> {{end}} </ol> <p>{{len .Results}} results in {{.Elapsed}}</p> </body> </html> `)) localhost:8080/search localhost:8080/search?q=golang localhost:8080/search?q=golang&output=json localhost:8080/search?q=golang&output=prettyjson 21

Validate the query func handleSearch(w http.ResponseWriter, req *http.Request) { log.Println("serving", req.URL) // Check the search query. query := req.FormValue("q") if query == "" { http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest) return } FormValue is a method on the type *http.Request : package http type Request struct {...} func (r *Request) FormValue(key string) string {...} query := req.FormValue("q") initializes a new variable query with

the type of the expression on the right hand side, string . 22

Fetch the search results import "golang.org/x/talks/content/2016/applicative/google" // Run the Google search. start := time.Now() results, err := google.Search(query) elapsed := time.Since(start) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } Search returns two values, a slice of results and an error.

The results are valid only if the error is nil. type error interface { Error() string // a useful human-readable error message } Errors may contain additional information, accessed via type assertions. 23

Structure the search results // Create the structured response. type response struct { Results []google.Result Elapsed time.Duration } resp := response{results, elapsed} The response type is defined locally within handleSearch . The google.Result type is exported from package google : package google type Result struct { Title, URL string } The resp variable is initialized to a response value using a composite struct literal. 24

Render the search results // Render the response. switch req.FormValue("output") { case "json": err = json.NewEncoder(w).Encode(resp) case "prettyjson": var b []byte b, err = json.MarshalIndent(resp, "", " ") if err == nil { _, err = w.Write(b) } default: // HTML err = responseTemplate.Execute(w, resp) } Each case writes bytes to an io.Writer : type Writer interface { Write(p []byte) (n int, err error) } http.ResponseWriter implements the io.Writer interface. 25

HTML templates operate on Go values // +build ignore,OMIT // The server program issues Google search requests. It serves on port 8080. // // The /search endpoint accepts these query params: // q=the Google search query // // For example, http://localhost:8080/search?q=golang serves the first // few Google search results for "golang". package main import ( "encoding/json" "fmt" "html/template" "log" "net/http" "time" "golang.org/x/talks/content/2016/applicative/google" ) func main() { http.HandleFunc("/search", handleSearch) // HL fmt.Println("serving on http://localhost:8080/search") log.Fatal(http.ListenAndServe("localhost:8080", nil)) } // handleSearch handles URLs like "/search?q=golang" by running a // Google search for "golang" and writing the results as HTML to w. // The query parameter "output" selects alternate output formats: // "json" for JSON, "prettyjson" for human-readable JSON. func handleSearch(w http.ResponseWriter, req *http.Request) { // HL log.Println("serving", req.URL) // Check the search query. query := req.FormValue("q") // HL if query == "" { http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest) return } // ENDQUERY OMIT // Run the Google search. start := time.Now() results, err := google.Search(query) // HL elapsed := time.Since(start) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // ENDSEARCH OMIT // Create the structured response. type response struct { Results []google.Result Elapsed time.Duration } resp := response{results, elapsed} // HL // ENDRESPONSE OMIT // Render the response. switch req.FormValue("output") { case "json": err = json.NewEncoder(w).Encode(resp) // HL case "prettyjson": var b []byte b, err = json.MarshalIndent(resp, "", " ") // HL if err == nil { _, err = w.Write(b) } default: // HTML err = responseTemplate.Execute(w, resp) // HL } // ENDRENDER OMIT if err != nil { log.Print(err) return } } var responseTemplate = template.Must(template.New("results").Parse(` <html> <head/> <body> <ol> {{range .Results}} <li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li> {{end}} </ol> <p>{{len .Results}} results in {{.Elapsed}}</p> </body> </html> `)) 26

That's it for the search handler All the packages are from the standard library: import ( "encoding/json" "fmt" "html/template" "log" "net/http" "time" ) Go servers scale well: each request runs in its own goroutine. Let's talk about concurrency. 27

Communicating Sequential Processes (Hoare, 1978) Concurrent programs are structured as independent processes that

execute sequentially and communicate by passing messages. Sequential execution is easy to understand. Async callbacks are not. Go primitives: goroutines, channels, and the select statement. 28

Goroutines Goroutines are like lightweight threads. They start with tiny stacks and resize as needed. Go programs can have hundreds of thousands of them. Start a goroutine using the go statement: go f(args) The Go runtime schedules goroutines onto OS threads. Blocked goroutines don't use a thread. 29

Channels Channels provide communication between goroutines. c := make(chan string) // goroutine 1 c <- "hello!" // goroutine 2 s := <-c fmt.Println(s) // "hello!" 30

Select A select statement blocks until communication can proceed. select { case x := <-in: fmt.Println("received", x) case out <- v: fmt.Println("sent", v) } Only the selected case runs. 31

Example: Google Search (backend) Q: What does Google search do? A: Given a query, return a page of search results (and some ads). Q: How do we get the search results? A: Send the query to Web search, Image search, YouTube, Maps, News, etc., then mix the results. How do we implement this? 32

Google Search: A fake framework We can simulate a back end search with a random timeout up to 100ms. var ( Web = FakeSearch("web", "The Go Programming Language", "http://golang.org") Image = FakeSearch("image", "The Go gopher", "https://blog.golang.org/gopher/gopher.png") Video = FakeSearch("video", "Concurrency is not Parallelism", "https://www.youtube.com/watch?v=cN_DpYBzKso") ) type SearchFunc func(query string) Result func FakeSearch(kind, title, url string) SearchFunc { return func(query string) Result { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) return Result{ Title: fmt.Sprintf("%s(%q): %s", kind, query, title), URL: url, } } } 33

Google Search: Test the framework // +build ignore package main import ( "fmt" "math/rand" "time" "golang.org/x/talks/content/2016/applicative/google" ) func init() { rand.Seed(time.Now().UnixNano()) } func main() { start := time.Now() results, err := google.Search("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed, err) } 34

Google Search (serial) The Search function takes a query and returns a slice of Results . Search invokes the Web, Image, and Video searches serially, then constructs the results slice. func Search(query string) ([]Result, error) { results := []Result{ Web(query), Image(query), Video(query), } return results, nil } // +build ignore package main import ( "fmt" "math/rand" "time" "golang.org/x/talks/content/2016/applicative/google" ) func init() { rand.Seed(time.Now().UnixNano()) } func main() { start := time.Now() results, err := google.Search("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed, err) } 35

Google Search (parallel) Run the Web, Image, and Video searches concurrently, and wait for all results. The func literals are closures over query and c . func SearchParallel(query string) ([]Result, error) { c := make(chan Result) go func() { c <- Web(query) }() go func() { c <- Image(query) }() go func() { c <- Video(query) }() return []Result{<-c, <-c, <-c}, nil } // +build ignore package main import ( "fmt" "math/rand" "time" "golang.org/x/talks/content/2016/applicative/google" ) func init() { rand.Seed(time.Now().UnixNano()) } func main() { start := time.Now() results, err := google.SearchParallel("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed, err) } 36

Google Search (timeout) Don't wait for slow servers. func SearchTimeout(query string, timeout time.Duration) ([]Result, error) { timer := time.After(timeout) c := make(chan Result, 3) go func() { c <- Web(query) }() go func() { c <- Image(query) }() go func() { c <- Video(query) }() var results []Result for i := 0; i < 3; i++ { select { case result := <-c: results = append(results, result) case <-timer: return results, errors.New("timed out") } } return results, nil // +build ignore package main import ( "fmt" "math/rand" "time" "golang.org/x/talks/content/2016/applicative/google" ) func init() { rand.Seed(time.Now().UnixNano()) } func main() { start := time.Now() results, err := google.SearchTimeout("golang", 80*time.Millisecond) elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed, err) } 37

Avoid timeout Q: How do we avoid discarding results from slow servers? A: Replicate the servers. Send requests to multiple replicas, and use the first response. func First(replicas ...SearchFunc) SearchFunc { return func(query string) Result { c := make(chan Result, len(replicas)) searchReplica := func(i int) { c <- replicas[i](query) } for i := range replicas { go searchReplica(i) } return <-c } } 38

Using the First function // +build ignore,OMIT package main import ( "fmt" "math/rand" "time" "golang.org/x/talks/content/2016/applicative/google" ) func init() { rand.Seed(time.Now().UnixNano()) } // START2 OMIT func main() { start := time.Now() search := google.First( google.FakeSearch("replica 1", "I'm #1!", ""), google.FakeSearch("replica 2", "#2 wins!", "")) result := search("golang") elapsed := time.Since(start) fmt.Println(result) fmt.Println(elapsed) } // STOP2 OMIT 39

Google Search (replicated) Reduce tail latency using replicated back ends. var ( replicatedWeb = First(Web1, Web2) replicatedImage = First(Image1, Image2) replicatedVideo = First(Video1, Video2) ) func SearchReplicated(query string, timeout time.Duration) ([]Result, error) { timer := time.After(timeout) c := make(chan Result, 3) go func() { c <- replicatedWeb(query) }() go func() { c <- replicatedImage(query) }() go func() { c <- replicatedVideo(query) }() // +build ignore package main import ( "fmt" "math/rand" "time" "golang.org/x/talks/content/2016/applicative/google" ) func init() { rand.Seed(time.Now().UnixNano()) } func main() { start := time.Now() results, err := google.SearchReplicated("golang", 80*time.Millisecond) elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed, err) } Go functions have simple, synchronous signatures.

The use of concurrency is encapsulated. 40

What just happened? In just a few simple transformations we used Go's concurrency primitives to convert a slow

sequential

failure-sensitive program into one that is fast

concurrent

replicated

robust. No locks. No condition variables. No futures. No callbacks. 41

Getting started 42

You're interested in Go. What next? Take the Go Tour online. tour.golang.org Then go deeper ... golang.org/wiki/Learn Still interested? Run a pilot project. 43

Run a pilot project Reduces the cost & risks of switching to a new technology,

while helping your organization discover the benefits. 1. Choose something small to write in Go (e.g., a microservice)

2. Build a prototype with a friend Find the libraries you need

Integrate with editors & IDEs

Integrate with build & test & deploy

Learn how to debug & profile your program 3. Compare Go to what you use today Isolate the language change; try not to change anything else. 4. Present results to the team 44

Go is designed for tooling Go tools meet you where you are. There's no one "Go IDE". IDE & editor integration: Eclipse, IntelliJ, VisualStudio, SublimeText, emacs, vim, ...

play.golang.org: online playground

gofmt : automatic formatting

: automatic formatting goimports : automatic updates of package imports

: automatic updates of package imports gocode : automatic completion

: automatic completion the go tool: automatic fetch & build

tool: automatic fetch & build guru : static analysis, bug finding, code navigation

: static analysis, bug finding, code navigation godoc.org: open source package index and docs 45

Where to Go next Take the Go Tour online. tour.golang.org Lots more material. golang.org/wiki/Learn Great community. golang.org/project 46