Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go - a simple programming environment by Andrew Gerrand

Go - a simple programming environment by Andrew Gerrand

Watch the talk here: https://vimeo.com/69237265

Railsberry

April 23, 2013
Tweet

More Decks by Railsberry

Other Decks in Technology

Transcript

  1. Go: a simple programming
    environment
    23 April 2013
    Andrew Gerrand
    Google

    View full-size slide

  2. Software should be simple

    View full-size slide

  3. Software can be simple

    View full-size slide

  4. What I mean by "simple"
    Small
    Readable
    Consistent
    Orthogonal
    Predictable
    Robust
    Useful by default

    View full-size slide

  5. Go at a glance
    Compiled
    Statically typed
    Clean syntax
    Simple type system
    Concurrency primitives
    Rich standard library
    Great tools
    Open source
    This talk is just a taste.

    View full-size slide

  6. Hello, Go
    package main
    import "fmt"
    func main() {
    fmt.Println("Hello, Go")
    } Run

    View full-size slide

  7. go
    Goroutines are lightweight threads that are managed by the Go runtime.
    To run a function in a new goroutine, just put "go" before the function call.
    package main
    import (
    "fmt"
    "time"
    )
    func main() {
    go say("let's go!", 3*time.Second)
    go say("ho!", 2*time.Second)
    go say("hey!", 1*time.Second)
    time.Sleep(4 * time.Second)
    }
    func say(text string, delay time.Duration) {
    time.Sleep(delay)
    fmt.Println(text)
    } Run

    View full-size slide

  8. chan
    Channels are typed conduits for sychronization and communication between
    goroutines.
    They're a versatile and expressive means of modelling concurrent processes.
    But we're not going to look at them today. (There's no time!)

    View full-size slide

  9. sync
    Channels are great, but sometimes other concurrency mechanisms are a better fit.
    The sync package provides mutexes, condition variables, and more useful primitives.
    func main() {
    wg := new(sync.WaitGroup)
    wg.Add(3)
    go say(wg, "let's go!", 3*time.Second)
    go say(wg, "ho!", 2*time.Second)
    go say(wg, "hey!", 1*time.Second)
    wg.Wait()
    }
    func say(wg *sync.WaitGroup, text string, delay time.Duration) {
    time.Sleep(delay)
    fmt.Println(text)
    wg.Done()
    } Run

    View full-size slide

  10. time
    The
    time
    package provides
    Time
    and
    Duration
    types, for expressing instants in time
    and periods of time.
    It also provides a
    Location
    type for expressing time zones.
    And, as we've already seen, it provides functions related to sleeping.
    birthday, _ := time.Parse("Jan 2 2006", "Nov 10 2009") // time.Time
    age := time.Since(birthday) // time.Duration
    fmt.Printf("Go is %d days old\n", age/(time.Hour*24)) Run
    t := time.Now()
    fmt.Println(t.In(time.UTC))
    home, _ := time.LoadLocation("Australia/Sydney")
    fmt.Println(t.In(home)) Run

    View full-size slide

  11. net/http (1/2)
    The net/http package provides an HTTP client.
    The client handles HTTP Keep-Alive using a pool of connections, by default.
    (This is configurable, of course.)
    func main() {
    r, err := http.Get("http://www.golang.org/")
    if err != nil {
    log.Fatal(err)
    }
    if r.StatusCode != http.StatusOK {
    log.Fatal(r.Status)
    }
    io.Copy(os.Stdout, r.Body)
    } Run

    View full-size slide

  12. net/http (2/2)
    The net/http package also provides an HTTP server.
    This is a high-performance, DoS-hardened, production-ready web server.
    It serves dl.google.com.
    func main() {
    http.HandleFunc("/", handler)
    err := http.ListenAndServe("localhost:8080", nil)
    if err != nil {
    log.Fatal(err)
    }
    }
    func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello, web")
    } Run

    View full-size slide

  13. html/template
    The
    html/template
    package provides an HTML templating system that automatically
    escapes content depending on its context.
    const html = `
    var foo = {{.Foo}};

    {{.Text}}

    `
    func main() {
    tmpl := template.Must(template.New("example").Parse(html))
    data := struct {
    Foo string
    URL, Text string
    }{
    Foo: `Some "quoted" string`,
    URL: `" onClick="alert('xss!');`,
    Text: "The <- operator is for channel sends and receives",
    }
    tmpl.Execute(os.Stdout, data)
    } Run

    View full-size slide

  14. flag
    The
    flag
    package provides a simple API for parsing command-line flags.
    Example invocation (a little different than GNU getopt):
    $ flag -message 'Hold on...' -delay 5m
    var (
    message = flag.String("message", "Hello!", "what to say")
    delay = flag.Duration("delay", 2*time.Second, "how long to wait")
    )
    func main() {
    flag.Parse()
    fmt.Println(*message)
    time.Sleep(*delay)
    } Run

    View full-size slide

  15. An example
    isgo1point1outyet.com (http://isgo1point1outyet.com)

    View full-size slide

  16. Structure
    The program has two parts that execute concurrently:
    a poller that continuously checks whether Go 1.1 has been tagged, and
    an HTTP server providing the user interface.

    View full-size slide

  17. Sharing state
    We must share state ("Is Go 1.1 out?") between the repo poller and the user interface.
    This global struct variable contains a sync.RWMutex and a boolean value:
    var state struct {
    sync.RWMutex
    yes bool // whether Go 1.1 has been tagged.
    }
    To read, take the read lock (multiple goroutines can do this simultaneously):
    state.RLock()
    yes := state.yes
    state.RUnlock()
    To write, take the write lock (only one goroutine can do this at a time):
    state.Lock()
    state.yes = true
    state.Unlock()

    View full-size slide

  18. Polling (1/2)
    When a go1.1 tag exists in the Go repository this URL will return a "200 OK" response:
    const changeURL = "https://code.google.com/p/go/source/detail?r=go1.1"
    The isTagged function returns true if the go1.1 tag exists.
    func isTagged() bool {
    r, err := http.Head(changeURL)
    if err != nil {
    log.Print(err)
    return false
    }
    return r.StatusCode == http.StatusOK
    }

    View full-size slide

  19. Polling (2/2)
    The poll function loops until isTagged returns true.
    Then it updates the state ("Go 1.1 is out!") and returns.
    func poll(period time.Duration) {
    for !isTagged() {
    time.Sleep(period)
    }
    state.Lock()
    state.yes = true
    state.Unlock()
    }

    View full-size slide

  20. Serving the user interface
    The
    handler
    function serves an HTTP request.
    It puts the
    state.yes
    and
    changeURL
    values into a struct,
    and uses the struct to render the template as the HTTP response.
    func handler(w http.ResponseWriter, r *http.Request) {
    state.RLock()
    data := struct {
    Yes bool
    URL string
    }{
    Yes: state.yes,
    URL: changeURL,
    }
    state.RUnlock()
    err := tmpl.Execute(w, data)
    if err != nil {
    log.Print(err)
    }
    }

    View full-size slide

  21. The HTML user interface
    The tmpl variable is a template that provides the HTML UI.
    It is a global variable, so the template is parsed just once at init time.
    var tmpl = template.Must(template.New("root").Parse(`

    Is Go 1.1 out yet?

    {{if .Yes}}
    YES!
    {{else}}
    No.
    {{end}}


    `))

    View full-size slide

  22. Putting it all together
    The
    main
    function starts polling in a new goroutine and sets up the web server.
    Some command-line flags enable run time configuration.
    var (
    httpAddr = flag.String("http", "localhost:8080", "Listen address")
    pollPeriod = flag.Duration("poll", 5*time.Second, "Poll period")
    )
    func main() {
    flag.Parse()
    go poll(*pollPeriod)
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(*httpAddr, nil))
    }
    The whole program is just 68 lines of code.

    View full-size slide

  23. One more thing

    View full-size slide

  24. expvar (1/2)
    The
    expvar
    package allows you to export variables via an HTTP handler
    registered at
    /debug/vars
    (http://localhost:8080/debug/vars) .
    package main
    import (
    "expvar"
    "log"
    "net/http"
    "time"
    )
    func main() {
    count := expvar.NewInt("count")
    go func() {
    for {
    count.Add(1)
    time.Sleep(time.Second)
    }
    }()
    log.Fatal(http.ListenAndServe("localhost:8080", nil))
    } Run

    View full-size slide

  25. expvar (1/2)
    var (
    hitCount = expvar.NewInt("hitCount")
    pollCount = expvar.NewInt("pollCount")
    pollError = expvar.NewString("pollError")
    pollErrorCount = expvar.NewInt("pollErrorCount")
    )
    func isTagged() bool {
    pollCount.Add(1)
    r, err := http.Head(changeURL)
    if err != nil {
    log.Print(err)
    pollError.Set(err.Error())
    pollErrorCount.Add(1)
    return false
    }
    return r.StatusCode == http.StatusOK
    }
    func handler(w http.ResponseWriter, r *http.Request) {
    hitCount.Add(1)

    View full-size slide

  26. Conclusion
    Simplicity revisited:
    Small
    Readable
    Consistent
    Orthogonal
    Predictable
    Robust
    Useful by default

    View full-size slide

  27. Stuff I didn't talk about
    Types and interfaces
    Concurrency in depth
    Packages and the build system
    Code formatting
    Testing
    Benchmarking and profiling
    Debugging
    Documentation

    View full-size slide

  28. Learn more about Go
    The Go web site
    golang.org (http://golang.org)
    The Go blog
    blog.golang.org (http://blog.golang.org)
    Go talks
    talks.golang.org (http://talks.golang.org)
    A Tour of Go
    tour.golang.org (http://tour.golang.org)

    View full-size slide

  29. Thank you
    23 April 2013
    Andrew Gerrand
    Google
    @enneff (http://twitter.com/enneff)
    [email protected] (mailto:[email protected])
    http://golang.org (http://golang.org)

    View full-size slide