偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

如何在測(cè)試中發(fā)現(xiàn)Goroutine泄漏

開(kāi)發(fā) 前端
使用runtime.Stack()方法獲取當(dāng)前運(yùn)行的所有g(shù)oroutine的棧信息,默認(rèn)定義不需要檢測(cè)的過(guò)濾項(xiàng),默認(rèn)定義檢測(cè)次數(shù)+檢測(cè)間隔,不斷周期進(jìn)行檢測(cè),最終在多次檢查后仍沒(méi)有找到剩下的goroutine則判斷沒(méi)有發(fā)生goroutine泄漏。

前言

哈嘍,大家好,我是asong;

眾所周知,gorourtine的設(shè)計(jì)是Go語(yǔ)言并發(fā)實(shí)現(xiàn)的核心組成部分,易上手,但是也會(huì)遭遇各種疑難雜癥,其中g(shù)oroutine泄漏就是重癥之一,其出現(xiàn)往往需要排查很久,有人說(shuō)可以使用pprof來(lái)排查,雖然其可以達(dá)到目的,但是這些性能分析工具往往是在出現(xiàn)問(wèn)題后借助其輔助排查使用的,有沒(méi)有一款可以防患于未然的工具嗎?當(dāng)然有,goleak他來(lái)了,其由 Uber 團(tuán)隊(duì)開(kāi)源,可以用來(lái)檢測(cè)goroutine泄漏,并且可以結(jié)合單元測(cè)試,可以達(dá)到防范于未然的目的,本文我們就一起來(lái)看一看goleak。

goroutine泄漏

不知道你們?cè)谌粘i_(kāi)發(fā)中是否有遇到過(guò)goroutine泄漏,goroutine泄漏其實(shí)就是goroutine阻塞,這些阻塞的goroutine會(huì)一直存活直到進(jìn)程終結(jié),他們占用的棧內(nèi)存一直無(wú)法釋放,從而導(dǎo)致系統(tǒng)的可用內(nèi)存會(huì)越來(lái)越少,直至崩潰!簡(jiǎn)單總結(jié)了幾種常見(jiàn)的泄漏原因:

  • Goroutine內(nèi)的邏輯進(jìn)入死循壞,一直占用資源
  • Goroutine配合channel/mutex使用時(shí),由于使用不當(dāng)導(dǎo)致一直被阻塞
  • Goroutine內(nèi)的邏輯長(zhǎng)時(shí)間等待,導(dǎo)致Goroutine數(shù)量暴增

接下來(lái)我們使用Goroutine+channel的經(jīng)典組合來(lái)展示goroutine泄漏;

func GetData() {
var ch chan struct{}
go func() {
<- ch
}()
}

func main() {
defer func() {
fmt.Println("goroutines: ", runtime.NumGoroutine())
}()
GetData()
time.Sleep(2 * time.Second)
}

這個(gè)例子是channel忘記初始化,無(wú)論是讀寫(xiě)操作都會(huì)造成阻塞,這個(gè)方法如果是寫(xiě)單測(cè)是檢查不出來(lái)問(wèn)題的:

func TestGetData(t *testing.T) {
GetData()
}

運(yùn)行結(jié)果:

=== RUN   TestGetData
--- PASS: TestGetData (0.00s)
PASS

內(nèi)置測(cè)試無(wú)法滿足,接下來(lái)我們引入goleak來(lái)測(cè)試一下。

goleak

github地址:https://github.com/uber-go/goleak

使用goleak主要關(guān)注兩個(gè)方法即可:VerifyNone、VerifyTestMain,VerifyNone用于單一測(cè)試用例中測(cè)試,VerifyTestMain可以在TestMain中添加,可以減少對(duì)測(cè)試代碼的入侵,舉例如下:

使用VerifyNone:

func TestGetDataWithGoleak(t *testing.T) {
defer goleak.VerifyNone(t)
GetData()
}

運(yùn)行結(jié)果:

=== RUN   TestGetDataWithGoleak
leaks.go:78: found unexpected goroutines:
[Goroutine 35 in state chan receive (nil chan), with asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector.GetData.func1 on top of the stack:
goroutine 35 [chan receive (nil chan)]:
asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector.GetData.func1()
/Users/go/src/asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector/main.go:12 +0x1f
created by asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector.GetData
/Users/go/src/asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector/main.go:11 +0x3c
]
--- FAIL: TestGetDataWithGoleak (0.45s)

FAIL

Process finished with the exit code 1

通過(guò)運(yùn)行結(jié)果看到具體發(fā)生goroutine泄漏的具體代碼段;使用VerifyNone會(huì)對(duì)我們的測(cè)試代碼有入侵,可以采用VerifyTestMain方法可以更快的集成到測(cè)試中:

func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}

運(yùn)行結(jié)果:

=== RUN   TestGetData
--- PASS: TestGetData (0.00s)
PASS
goleak: Errors on successful test run: found unexpected goroutines:
[Goroutine 5 in state chan receive (nil chan), with asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector.GetData.func1 on top of the stack:
goroutine 5 [chan receive (nil chan)]:
asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector.GetData.func1()
/Users/go/src/asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector/main.go:12 +0x1f
created by asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector.GetData
/Users/go/src/asong.cloud/Golang_Dream/code_demo/goroutine_oos_detector/main.go:11 +0x3c
]

Process finished with the exit code 1

VerifyTestMain的運(yùn)行結(jié)果與VerifyNone有一點(diǎn)不同,VerifyTestMain會(huì)先報(bào)告測(cè)試用例執(zhí)行結(jié)果,然后報(bào)告泄漏分析,如果測(cè)試的用例中有多個(gè)goroutine泄漏,無(wú)法精確定位到發(fā)生泄漏的具體test,需要使用如下腳本進(jìn)一步分析:

# Create a test binary which will be used to run each test individually
$ go test -c -o tests

# Run each test individually, printing "." for successful tests, or the test name
# for failing tests.
$ for test in $(go test -list . | grep -E "^(Test|Example)"); do ./tests -test.run "^$test\$" &>/dev/null && echo -n "." || echo -e "\n$test failed"; done

這樣會(huì)打印出具體哪個(gè)測(cè)試用例失敗。

goleak實(shí)現(xiàn)原理

從VerifyNone入口,我們查看源代碼,其調(diào)用了Find方法:

// Find looks for extra goroutines, and returns a descriptive error if
// any are found.
func Find(options ...Option) error {
// 獲取當(dāng)前goroutine的ID
cur := stack.Current().ID()

opts := buildOpts(options...)
var stacks []stack.Stack
retry := true
for i := 0; retry; i++ {
// 過(guò)濾無(wú)用的goroutine
stacks = filterStacks(stack.All(), cur, opts)

if len(stacks) == 0 {
return nil
}
retry = opts.retry(i)
}

return fmt.Errorf("found unexpected goroutines:\n%s", stacks)
}

我們?cè)诳匆幌耭ilterStacks方法:

// filterStacks will filter any stacks excluded by the given opts.
// filterStacks modifies the passed in stacks slice.
func filterStacks(stacks []stack.Stack, skipID int, opts *opts) []stack.Stack {
filtered := stacks[:0]
for _, stack := range stacks {
// Always skip the running goroutine.
if stack.ID() == skipID {
continue
}
// Run any default or user-specified filters.
if opts.filter(stack) {
continue
}
filtered = append(filtered, stack)
}
return filtered
}

這里主要是過(guò)濾掉一些不參與檢測(cè)的goroutine stack,如果沒(méi)有自定義filters,則使用默認(rèn)的filters:

func buildOpts(options ...Option) *opts {
opts := &opts{
maxRetries: _defaultRetries,
maxSleep: 100 * time.Millisecond,
}
opts.filters = append(opts.filters,
isTestStack,
isSyscallStack,
isStdLibStack,
isTraceStack,
)
for _, option := range options {
option.apply(opts)
}
return opts
}

從這里可以看出,默認(rèn)檢測(cè)20次,每次默認(rèn)間隔100ms;添加默認(rèn)filters;

總結(jié)一下goleak的實(shí)現(xiàn)原理:

使用runtime.Stack()方法獲取當(dāng)前運(yùn)行的所有g(shù)oroutine的棧信息,默認(rèn)定義不需要檢測(cè)的過(guò)濾項(xiàng),默認(rèn)定義檢測(cè)次數(shù)+檢測(cè)間隔,不斷周期進(jìn)行檢測(cè),最終在多次檢查后仍沒(méi)有找到剩下的goroutine則判斷沒(méi)有發(fā)生goroutine泄漏。

總結(jié)

本文我們分享了一個(gè)可以在測(cè)試中發(fā)現(xiàn)goroutine泄漏的工具,但是其還是需要完備的測(cè)試用例支持,這就暴露出測(cè)試用例的重要性,朋友們好的工具可以助我們更快的發(fā)現(xiàn)問(wèn)題,但是代碼質(zhì)量還是掌握在我們自己的手中,加油吧,少年們~。

好啦,本文到這里就結(jié)束了,我是asong,我們下期見(jiàn)。

責(zé)任編輯:武曉燕 來(lái)源: Golang夢(mèng)工廠
相關(guān)推薦

2014-04-24 16:21:50

LinuxIP地址沖突

2018-01-29 11:10:47

LinuxUnix網(wǎng)絡(luò)取證工具

2022-09-20 12:53:15

編程語(yǔ)言漏洞

2023-11-08 08:31:37

2020-01-03 10:19:28

goroutine泄漏系統(tǒng)

2022-08-10 18:23:39

Python軟件包索引惡意軟件

2018-10-16 10:13:06

2023-07-13 23:23:24

2021-04-12 17:44:49

APKPure惡意軟件Android

2024-04-01 07:00:00

區(qū)塊鏈深度偽造

2022-05-12 14:08:56

數(shù)字孿生制造業(yè)醫(yī)療保健

2022-02-22 14:43:16

區(qū)塊鏈游戲加密貨幣

2009-10-28 10:38:16

IDC調(diào)查虛擬化

2022-10-21 13:57:46

2020-12-24 17:16:16

物聯(lián)網(wǎng)保護(hù)環(huán)境IOT

2022-02-09 10:04:35

財(cái)務(wù)自動(dòng)化深度學(xué)習(xí)機(jī)器學(xué)習(xí)

2018-10-10 19:50:18

區(qū)塊鏈GDPR數(shù)據(jù)

2022-01-28 23:11:40

區(qū)塊鏈加密貨幣技術(shù)

2021-02-22 11:44:43

機(jī)器學(xué)習(xí)數(shù)據(jù)泄露學(xué)習(xí)

2018-10-26 08:40:20

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)