The Go X repository
And a top 5 of my favorite packages
Christian Joergensen
Co-founder, CTO at Ubivox Technologies
Christian Joergensen
Co-founder, CTO at Ubivox Technologies
golang.org/x/<project>https://github.com/golang/<project> and https://go.googlesource.com/<project>Each project contains multiple packages.
golang.org/x/crypto - various high level crypto toolsgolang.org/x/exp - experiments (e.g. shiny)golang.org/x/image - font and draw support for image.Image golang.org/x/mobile - Go apps for Android and IOSgolang.org/x/net - extensions for the std net packagesgolang.org/x/sync - high level syncronization toolsgolang.org/x/text - encoding and transformation of textgolang.org/x/time - rate limiting
And various other stuff in: arch, blog, oauth2, perf, review, sys, talks, term, tools
My personal top 5 favorite packages from /x/.
With a small introduction for each of them.
5A token bucket rate limiter for events defined by two parameters:
type Limit float64 func NewLimiter(r Limit, b int) *Limiter
r: Events per secondb: Permit bursts of this sizeConvenience function:
func Every(interval time.Duration) Limit
Thus, we could write:
l := rate.NewLimiter(rate.Every(25 * time.Minute), 1)
Block until an operation may proceed.
l := rate.NewLimiter(1, 1)
ctx := context.Background()
for {
l.Wait(ctx)
// Do your thing
}Context support for:
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
9How 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().
Example:
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 }
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.
14Start 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/debug/requests and /debug/eventshttp.DefaultServeMuxJust 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:
family: The (qualified) handler nametitle: The request path
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) }
Run some requests and point your web browser to your server at /debug/requests.
Demo!
19
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)
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 }
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() }
Why is this better than the a naive implementation using only sync.WaitGroup?
Wait().Add()'ing enough delta to your WaitGroup.Wait(), but once the individual subtasks has been cancelled, it will return.net.Listener support, to become fully automatic and enable clever one-liners using http.Serve :)This is awesome. TLS has never been easier!
Let's start with a simple Hello World server...
26package 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)) }
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!
28Almost feels like magic. But here's what happens:
HelloRequest message in the TLS handshakeautocert.NewListenerTakes 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.
29Christian Joergensen
Co-founder, CTO at Ubivox Technologies