プロファイラ HTTP Server-Timing のGoパッケージ go-server-timing


サーバサイドの処理のプロファイリングをChromeのDevtoolで可視化するライブラリです。
go-server-timing

そもそも論として

HTTPプロトコルヘッダーには、Server-Timing というものがあります。ここにサーバ側で計測した値をセットして、HTTPレスポンスに一緒に仕込んでブラウザに返せば、Chrome Devtoolがいい感じに可視化してくれるわけです。

こんな感じに。

コードサンプル

実行したソースコードです。(README.mdからママ)

package main

import (
    "fmt"
    "math/rand"
    "net/http"
    "sync"
    "time"

    servertiming "github.com/mitchellh/go-server-timing"
)

func main() {
    // Our handler. In a real application this might be your root router,
    // or some subset of your router. Wrapping this ensures that all routes
    // handled by this handler have access to the server timing header struct.
    var h http.Handler = http.HandlerFunc(handler)

    // Wrap our handler with the server timing middleware
    h = servertiming.Middleware(h, nil)

    // Start!
    http.ListenAndServe(":8080", h)
}

func handler(w http.ResponseWriter, r *http.Request) {
    // Get our timing header builder from the context
    timing := servertiming.FromContext(r.Context())

    // Imagine your handler performs some tasks in a goroutine, such as
    // accessing some remote service. timing is concurrency safe so we can
    // record how long that takes. Let's simulate making 5 concurrent requests
    // to various servicse.
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        name := fmt.Sprintf("service-%d", i)
        go func(name string) {
            // This creats a new metric and starts the timer. The Stop is
            // deferred so when the function exits it'll record the duration.
            defer timing.NewMetric(name).Start().Stop()
            time.Sleep(random(25, 75))
            wg.Done()
        }(name)
    }
    // Imagine this is just some blocking code in your main handler such
    // as a SQL query. Let's record that.
    m := timing.NewMetric("sql").WithDesc("SQL query").Start()
    time.Sleep(random(20, 50))
    m.Stop()

    // Wait for the goroutine to end
    wg.Wait()

    // You could continue recording more metrics, but let's just return now
    w.WriteHeader(200)
    w.Write([]byte("Done. Check your browser inspector timing details."))
}

func random(min, max int) time.Duration {
    return (time.Duration(rand.Intn(max-min) + min)) * time.Millisecond
}

コードの説明

41行目の

defer timing.NewMetric(name).Start().Stop()

でプロファイラを仕込んでいます。

Goのdeferで、関数をチェインした場合、最後の.Stop()のみdeferされ、.Start()は即時実行されることに注意してください。

また、deferのスコープは関数になります。38行目のgo func