The Go X repository

And a top 5 of my favorite packages

Christian Joergensen

Co-founder, CTO at Ubivox Technologies

About me

2

What is the /x/ repository

3

/x/ projects

Each project contains multiple packages.

And various other stuff in: arch, blog, oauth2, perf, review, sys, talks, term, tools

4

A top 5 of /x/ packages

My personal top 5 favorite packages from /x/.

With a small introduction for each of them.

5

Number 5: /x/time/rate

6

Number 5: /x/time/rate

A token bucket rate limiter for events defined by two parameters:

type Limit float64

func NewLimiter(r Limit, b int) *Limiter

Convenience function:

func Every(interval time.Duration) Limit

Thus, we could write:

l := rate.NewLimiter(rate.Every(25 * time.Minute), 1)
7

Number 5: /x/time/rate (Wait)

Block until an operation may proceed.

l := rate.NewLimiter(1, 1)
ctx := context.Background()

for {
    l.Wait(ctx)
    // Do your thing
}

Context support for:

8

Number 5: /x/time/rate (Allow)

Check if an operation may proceed

l := rate.NewLimiter(1, 1)

for {
    if !l.Allow() {
        continue
    }
    // Do your thing
}

If you intend to skip the operation, if it may not

9

Number 5: /x/time/rate (Reserve)

How long should I wait before the operation may proceed

l := rate.NewLimiter(1, 1)

for {
    r := l.Reserve()
    time.Sleep(r.Delay())

    // Do your thing
}

If you intend to wait yourself and potentially slow down processing upstream.

A reservation can be cancelled by calling r.Cancel().

10

Number 4: /x/crypto/ssh

11

Number 4: /x/crypto/ssh

Example:

12

Number 4: /x/crypto/ssh (Parse private key)

First things first. Parse the private key using the provided helper function:

func loadRSAKey() ssh.Signer {

    key, err := ioutil.ReadFile("/home/razor/.ssh/id_ed25519")
    if err != nil {
        log.Fatalf("unable to read private key: %v", err)
    }

    signer, err := ssh.ParsePrivateKey(key)
    if err != nil {
        log.Fatalf("unable to parse private key: %v", err)
    }

    return signer
}
13

Number 4: /x/crypto/ssh (SSH client connection)

Configure the SSH client connection

    config := &ssh.ClientConfig{
        User: "razor",
        Auth: []ssh.AuthMethod{
            ssh.PublicKeys(loadRSAKey()),
            ssh.Password("hunter2"),
        },
        HostKeyCallback: ssh.FixedHostKey(hostKey),
    }

    client, err := ssh.Dial("tcp", "cerebro.technobabble.dk:22", config)
    if err != nil {
        log.Fatalf("failed to dial: %v", err)
    }

Multiple sessions can re-use the same client connection. Will be multiplexed onto single TCP connection.

The connection can be shared between multiple go routines.

14

Number 4: /x/crypto/ssh (SSH session and command execution)

Start a session and execute a command

    session, err := client.NewSession()
    if err != nil {
        log.Fatalf("failed to create session: %v", err)
    }
    defer session.Close()

    var b bytes.Buffer
    session.Stdout = &b
    if err := session.Run("/usr/bin/whoami"); err != nil {
        log.Fatalf("failed to run: %v", err)
    }

    fmt.Println(b.String())

And like that, Go does SSH.

15

Number 3: /x/net/trace

16

Number 3: /x/net/trace

Just a quick intro, so I will focus only on the request tracing part here.

Add a trace to your handler function:

func New(family, title string) Trace

Conventionally, for HTTP requests:

17

Number 3: /x/net/trace (Instrumenting handlers)

To instrument an http.HandlerFunc, use something like:

func Handler(rw http.ResponseWriter, req *http.Request) {

    tr := trace.New("mypkg.Handler", req.URL.Path)
    defer tr.Finish()

    id := req.URL.Query().Get("id")

    tr.LazyPrintf("looking up object id:%v in database", id)

    obj, err := lookupObj(id)
    if err != nil {
        tr.LazyPrintf("database lookup failed: %v", err)
        tr.SetError()
    }

    tr.LazyPrintf("rendering response for object id:%v", id)

    render(rw, obj)

}
18

Number 3: /x/net/trace (Tracing requests)

Run some requests and point your web browser to your server at /debug/requests.

Demo!

19

Number 2: /x/sync/errgroup

20

Number 2: /x/sync/errgroup

Basically a fancy sync.WaitGroup.

Perform a set of subtasks in a group of goroutines with:

Only two methods:

func (g *Group) Go(f func() error)
func (g *Group) Wait() error

And a constructor for context.Context support:

func WithContext(ctx context.Context) (*Group, context.Context)
21

Number 2: /x/sync/errgroup (The work)

func analyzeURL(ctx context.Context, url string) (*Result, error) {

    // Construct request
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return nil, err
    }

    // Run request
    resp, err := http.Do(req.WithContext(ctx))
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    // TODO: read resp.Body and analyze

    return &Result{}, nil

}
22

Number 2: /x/sync/errgroup (Usage)

func analyze(ctx context.Context) error {

    g, ctx := errgroup.WithContext(ctx)

    var urls = []string{
        "http://www.golang.org/",
        "http://www.google.com/",
        "http://www.example.com/",
    }

    for _, url := range urls {
        url := url
        g.Go(func() error {
            _, err := analyzeURL(ctx, url)
            return err
        })
    }

    return g.Wait()

}
23

Number 2: /x/sync/errgroup (A better WaitGroup)

Why is this better than the a naive implementation using only sync.WaitGroup?

24

Number 1: /x/crypto/acme/autocert

25

Number 1: /x/crypto/acme/autocert

This is awesome. TLS has never been easier!

Let's start with a simple Hello World server...

26

Number 1: /x/crypto/acme/autocert (Hello world server)

package main

import (
    "io"
    "log"
    "net"
    "net/http"
)

func main() {

    http.HandleFunc("/hello", func(rw http.ResponseWriter, req *http.Request) {
        io.WriteString(rw, "Hello World")
    })

    ln, err := net.Listen("tcp", ":80")
    if err != nil {
        log.Fatalf("couldn't listen: %v", err)
    }

    log.Fatal(http.Serve(ln, nil))

}
27

Number 1: /x/crypto/acme/autocert (TLS server)

package main

import (
    "io"
    "log"
    "net/http"

    "golang.org/x/crypto/acme/autocert"
)

func main() {

    http.HandleFunc("/hello", func(rw http.ResponseWriter, req *http.Request) {
        io.WriteString(rw, "Hello World")
    })

    log.Fatal(http.Serve(autocert.NewListener("example.com"), nil))

}

It's even shorter!

28

Number 1: /x/crypto/acme/autocert (The gory details)

Almost feels like magic. But here's what happens:

Takes less than five seconds on the first request.

Used in production at Ubivox to provide SSL for custom domains for our customers and serves millions of requests each day.

29

Thank you

Christian Joergensen

Co-founder, CTO at Ubivox Technologies

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)