ゴルーチン リーク
Go言語はめちゃめちゃ簡単にゴルーチン(スレッドのようなもの)を作れるのが魅力です。
ゴルーチンはメインフローとは別に切り離されて実行されるので、デッドロックなどでゴルーチンの処理が途中で止まっていても気づかず、「終了したと思っていたが、実は実行中で残っていました」ということがありました。
それ以来、ゴルーチンの起動数を定期的にログに出力して、ゴルーチンのリークが起こっていないかチェックしているのですが、その作業が面倒くさくなってきました。
もっと楽したい
GCPのCloud Monitoringは主に、GCPの各種サービスの状態モニタリングを行う機能なのですが、アプリの状態モニタリングも行えるので、それを使ってゴルーチンの起動数を監視することにしました。
方針
Cloud Monitoringに、Go言語のpprofの値を送るようにして、Cloud Monitoring上でゴルーチンの起動数をグラフ表示します。
プログラムからCloud Monitoringに値を送るには、OpenCensusというオープンソースのライブラリを使うのですが、色々お膳立てが必要で、ちょっとした値を送りたい場合でも、結構コードを書く必要があります。
しかし、OpenCensusのGo言語のライブラリには、pprofの値をよしなに取得して送る機能があり、それを使えば数行のコードでできるので、今回はそれをそのまま使いました。
手順
権限設定
Cloud Monitoringにデータを送るには、「モニタリング管理者」権限が必要なので、アプリに付与しておきます。
サンプルプログラム
package main
import (
"context"
"log"
"os"
"time"
"contrib.go.opencensus.io/exporter/stackdriver"
"go.opencensus.io/metric/metricexport"
"go.opencensus.io/plugin/runmetrics"
)
func main() {
//////////////////////////////////////////////////
// Cloud Monitoringにpprofを送る
//////////////////////////////////////////////////
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "./service-account-key.json")
// exporter(Cloud Monitoring)
exporter, err := stackdriver.NewExporter(stackdriver.Options{
// Cloud Montoringからの検索用文字列
MetricPrefix: "test-app-go-pprof",
// データ送信頻度(60秒以上)
ReportingInterval: 120 * time.Second,
})
if err != nil {
log.Fatal(err)
}
defer exporter.Flush()
exporter.StartMetricsExporter()
defer exporter.StopMetricsExporter()
// metrics(pprof)
err = runmetrics.Enable(runmetrics.RunMetricOptions{
EnableCPU: true,
EnableMemory: true,
})
if err != nil {
log.Fatal(err)
}
// metrics -> exporter
metricexport.NewReader().ReadAndExport(exporter)
//////////////////////////////////////////////////
// ゴルーチン リーク テスト
//////////////////////////////////////////////////
ctx := context.Background()
for {
go func() {
var a [1000 * 1000 * 10]byte // メモリも消費
_ = a
<-ctx.Done() // 停止
}()
time.Sleep(60 * time.Second)
}
}
補足説明
- OpenCensusはデータ部分(metrics)と送信部分(exporter)に分かれています。
- exporterをCloud Monitoring(旧名stackdriver)にして、runmetricsでpprofからmetrictを生成するようにします。
- exporter(Cloud Monitoring)がmetrics(pprof)を読み込んで出力するよう紐付けします。
- Cloud Monitoringには色んな種類のデータが送られているので、識別しやすいように「MetricPrefix」を付けます。
Cloud Monitoring
プログラムを実行すると、Cloud Monitoringに
custom.googleapis.com/opencensus/test-app-go-pprof/process/cpu_goroutines
という名前で、ゴルーチン数が記録されるので、[Cloud Monitoring]-[Metrics Explorer]
で見ることができます。
応用
他のpprofの値も送られるので、ダッシュボードでゴルーチン数とメモリの使用量をならべて表示するといったことも、プログラムの修正無しにできます。
ゴルーチンの数がしきい値を超えたら、Slackに通知などもできます。
感想など
これでゴルーチンのリーク監視作業から解放されました。