В Go может быть запущенно больше миллиона goroutine, и этот механизм вызывает восторг до того момента, пока не появляется какая-либо проблема. Например, утечка памяти или dead-lock.
Первое, что хочется сделать это разобраться, посмотреть на то, что же происходит.
Это можно сделать так.
package main
import (
"bytes"
"fmt"
"runtime/pprof"
)
func main() {
profiler := pprof.Lookup("goroutine")
var buf bytes.Buffer
profiler.WriteTo(&buf, 1)
fmt.Println(buf.String())
}
goroutine profile: total 1
1 @ 0xb1620 0xb1340 0xac9c0 0x200c0 0x59f80 0x98d61
# 0xb161f runtime/pprof.writeRuntimeProfile+0xdf /usr/local/go/src/runtime/pprof/pprof.go:614
# 0xb133f runtime/pprof.writeGoroutine+0x9f /usr/local/go/src/runtime/pprof/pprof.go:576
# 0xac9bf runtime/pprof.(*Profile).WriteTo+0xff /usr/local/go/src/runtime/pprof/pprof.go:298
# 0x200bf main.main+0xbf /tmp/sandbox627252447/main.go:12
# 0x59f7f runtime.main+0x39f /usr/local/go/src/runtime/proc.go:183
https://play.golang.org/p/ysl_avotLS
Или так:
package main
import (
"fmt"
"runtime"
)
func main() {
b1 := make([]byte, 1<<20)
runtime.Stack(b1, true)
s1 := string(b1)
fmt.Println(s1)
}
goroutine 1 [running]:
main.main()
/tmp/sandbox952340390/main.go:10 +0x80
https://play.golang.org/p/FCbzn2_DlQ
Мы видим, что в данных примерах запущенна одна гоурутина и ее стек трейс, но если у нас 20-30 одинаковых goroutine, то возникает вопрос кого они обслуживают? Если это сайт, то может быть пользователь, который ушел, а goroutine не прекратила своё существование, и как следствие не освобождает память?
Появляется желание давать гоурутинам названия и в название вписать свякую полезную инфу, как например Id пользователя, которого она обслуживаниет. Но в Go непросто так нет функции SetGoroutineName
или GetGoroutineId
. Такие функции — зло по мнению создателей языка Go. Трудно не согласиться с такими людми как Rob и Ken. Но зло это не потому, что вообще эти функции зло, а потому, что эти функции программисты начнут использовать неправильно. Не для отладки приложений в момент утечки памяти, а просто как goroutine local storage. То есть, в логике программы плохо использовать такие штуки, но если накосячили, то все же ошибку надо как-то найти.
PyraDebug
Значит нужно давать имена горутинам, но что-бы логика приложения от этого не зависела. Так и родилась pyradebug
go get -u github.com/CossackPyra/pyradebug
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/CossackPyra/pyradebug"
)
var pyraDebug *pyradebug.PyraDebug
func main() {
pyraDebug = pyradebug.InitPyraDebug()
pyraDebug.Enable = true
for i := 0; i < 10; i++ {
go func(i int) {
pyraDebug.SetGoroutineName(fmt.Sprintf("sleep func %d", i))
for {
time.Sleep(time.Second)
}
}(i)
}
http.HandleFunc("/goroutine", handle_goroutine)
log.Fatal(http.ListenAndServe(":8765", nil))
}
func handle_goroutine(w http.ResponseWriter, r *http.Request) {
agi := pyraDebug.ListGoroutines(1<<20, true)
w.Header().Set("Content-Type", "text/plain")
for i, gi := range agi {
fmt.Fprintf(w, "%dt%st%st%sn", i, gi.Id, gi.Name, gi.Status)
}
}
# http://127.0.0.1:8765/goroutine
0 goroutine 31 running
1 goroutine 1 IO wait
2 goroutine 17 syscall, locked to thread
3 goroutine 20 sleep func 0 sleep
4 goroutine 21 sleep func 1 sleep
5 goroutine 22 sleep func 2 sleep
6 goroutine 23 sleep func 3 sleep
7 goroutine 24 sleep func 4 sleep
8 goroutine 25 sleep func 5 sleep
9 goroutine 26 sleep func 6 sleep
10 goroutine 27 sleep func 7 sleep
11 goroutine 28 sleep func 8 sleep
12 goroutine 29 sleep func 9 sleep
Важной особенностью библиотеки является pyraDebug.Enable = true
, то есть после отладки вы просто ее отключаете и она перестает что-либо делать и значит полагаться на SetGoroutineName
в логике нельзя.
Изначально я хотел указывать еще имя гоурутины, что породила её, но на практике мне достаточно только имя, исходный код 127 строки. Его легко форкнуть, разобраться и доработать.
Библитека зависит от текстового формата, который выдает runtime.Stack
и может перестать работать в будущем, что так-же хорошо, чтобы разработчики не полагались бездумно на эту библитеку и использовали ее только для отладки.
PS. Всегда интересно узнать, что сделал велосепед
https://github.com/CossackPyra/pyradebug
Автор: pyra