SetGoroutineName

от автора

В Go может быть запущенно больше миллиона goroutine, и этот механизм вызывает восторг до того момента, пока не появляется какая-либо проблема. Например, утечка памяти или dead-lock.

Первое, что хочется сделать это разобраться, посмотреть на то, что же происходит.
image

это можно сделать так

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, "%d\t%s\t%s\t%s\n", 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

ссылка на оригинал статьи https://habrahabr.ru/post/318220/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *