国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術(shù)及教程分享平臺!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Golang - 定位并修復(fù) Go 中的內(nèi)存泄露問題

定位并修復(fù) Go 中的內(nèi)存泄露問題

2021-11-24 11:29Go語言中文網(wǎng) Golang

Go 是一門帶 GC 的語言,這篇文章回顧了我如何發(fā)現(xiàn)內(nèi)存泄漏、如何修復(fù)它,以及我如何修復(fù) Google 示例 Go 代碼中的類似問題,以及我們?nèi)绾胃倪M(jìn)我們的庫以防止將來發(fā)生這種情況,感興趣的朋友一起看看吧

Go 是一門帶 GC 的語言,因此,大家很容易認(rèn)為它不會有內(nèi)存泄露問題。 大部分時候確實(shí)不會,但如果有些時候使用不注意,也會導(dǎo)致泄露。

本文案例來自谷歌云的代碼,探討如何找到并修復(fù) Go 中的內(nèi)存泄露。(確切來說是因?yàn)橘Y源泄露導(dǎo)致的內(nèi)存泄露,除了本文介紹的,還有一些其他泄露的情況)

這篇文章回顧了我如何發(fā)現(xiàn)內(nèi)存泄漏、如何修復(fù)它,以及我如何修復(fù) Google 示例 Go 代碼中的類似問題,以及我們?nèi)绾胃倪M(jìn)我們的庫以防止將來發(fā)生這種情況。

Google Cloud Go 客戶端庫 [1] 通常在后臺使用 gRPC 來連接 Google Cloud API。創(chuàng)建 API 客戶端時,庫會初始化與 API 的連接,然后保持該連接處于打開狀態(tài),直到你調(diào)用 Client.Close 。

?
1
2
3
client, err := api.NewClient()
// Check err.
defer client.Close()

客戶端可以安全地同時使用,所以你應(yīng)該保持相同 Client 直到你的任務(wù)完成。但是,如果在應(yīng)該 Close 的時候不 Close client 會發(fā)生什么呢?

會出現(xiàn)內(nèi)存泄漏。底層連接永遠(yuǎn)不會被清理。

Google 有一堆 GitHub 自動化機(jī)器人來幫助管理數(shù)百個 GitHub 存儲庫。我們的一些機(jī)器人通過在 Cloud Run [2] 上運(yùn)行的 Go 服務(wù)器 [3] 代理它們的請求。我們的內(nèi)存使用看起來像一個經(jīng)典的鋸齒形內(nèi)存泄漏:

我通過向服務(wù)器添加 pprof.Index 處理程序開始調(diào)試:

?
1
mux.HandleFunc("/debug/pprof/", pprof.Index)

`pprof` [4] 提供運(yùn)行時 profiling 數(shù)據(jù),如內(nèi)存使用情況。有關(guān)更多信息,請參閱 Go 官方博客上的 profiling Go 程序 [5] 。

然后,我在本地構(gòu)建并啟動了服務(wù)器:

?
1
2
$ go build
$ PROJECT_ID=my-project PORT=8080 ./serverless-scheduler-proxy

然后向服務(wù)器發(fā)送一些請求:

?
1
2
3
4
for i in {1..5}; do
  curl --header "Content-Type: application/json" --request POST --data '{"name": "HelloHTTP", "type": "testing", "location": "us-central1"}' localhost:8080/v0/cron
  echo " -- $i"
done

確切的有效負(fù)載和端點(diǎn)特定于我們的服務(wù)器,與本文無關(guān)。

為了獲得正在使用的內(nèi)存的基線,我收集了一些初始 pprof 數(shù)據(jù):

curl http://localhost:8080/debug/pprof/heap > heap.0.pprof

檢查輸出,你可以看到一些內(nèi)存使用情況,但沒有什么會立即成為一個大問題(這很好!我們剛剛啟動了服務(wù)器!):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ go tool pprof heap.0.pprof
File: serverless-scheduler-proxy
Type: inuse_space
Time: May 4, 2021 at 9:33am (EDT)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top10
Showing nodes accounting for 2129.67kB, 100% of 2129.67kB total
Showing top 10 nodes out of 30
      flat  flat%   sum%        cum   cum%
 1089.33kB 51.15% 51.15%  1089.33kB 51.15%  google.golang.org/grpc/internal/transport.newBufWriter (inline)
  528.17kB 24.80% 75.95%   528.17kB 24.80%  bufio.NewReaderSize (inline)
  512.17kB 24.05%   100%   512.17kB 24.05%  google.golang.org/grpc/metadata.Join
         0     0%   100%   512.17kB 24.05%  cloud.google.com/go/secretmanager/apiv1.(*Client).AccessSecretVersion
         0     0%   100%   512.17kB 24.05%  cloud.google.com/go/secretmanager/apiv1.(*Client).AccessSecretVersion.func1
         0     0%   100%   512.17kB 24.05%  github.com/googleapis/gax-go/v2.Invoke
         0     0%   100%   512.17kB 24.05%  github.com/googleapis/gax-go/v2.invoke
         0     0%   100%   512.17kB 24.05%  google.golang.org/genproto/googleapis/cloud/secretmanager/v1.(*secretManagerServiceClient).AccessSecretVersion
         0     0%   100%   512.17kB 24.05%  google.golang.org/grpc.(*ClientConn).Invoke
         0     0%   100%  1617.50kB 75.95%  google.golang.org/grpc.(*addrConn).createTransport

下一步是向服務(wù)器發(fā)送一堆請求,看看我們是否可以 (1) 重現(xiàn)可能的內(nèi)存泄漏和 (2) 確定泄漏是什么。

發(fā)送 500 個請求:

?
1
2
3
4
for i in {1..500}; do
  curl --header "Content-Type: application/json" --request POST --data '{"name": "HelloHTTP", "type": "testing", "location": "us-central1"}' localhost:8080/v0/cron
  echo " -- $i"
done

收集和分析更多 pprof 數(shù)據(jù):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ curl http://localhost:8080/debug/pprof/heap > heap.6.pprof
$ go tool pprof heap.6.pprof
File: serverless-scheduler-proxy
Type: inuse_space
Time: May 4, 2021 at 9:50am (EDT)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top10
Showing nodes accounting for 94.74MB, 94.49% of 100.26MB total
Dropped 26 nodes (cum <= 0.50MB)
Showing top 10 nodes out of 101
      flat  flat%   sum%        cum   cum%
   51.59MB 51.46% 51.46%    51.59MB 51.46%  google.golang.org/grpc/internal/transport.newBufWriter
   19.60MB 19.55% 71.01%    19.60MB 19.55%  bufio.NewReaderSize
    6.02MB  6.01% 77.02%     6.02MB  6.01%  bytes.makeSlice
    4.51MB  4.50% 81.52%    10.53MB 10.51%  crypto/tls.(*Conn).readHandshake
       4MB  3.99% 85.51%     4.50MB  4.49%  crypto/x509.parseCertificate
       3MB  2.99% 88.51%        3MB  2.99%  crypto/tls.Client
    2.50MB  2.49% 91.00%     2.50MB  2.49%  golang.org/x/net/http2/hpack.(*headerFieldTable).addEntry
    1.50MB  1.50% 92.50%     1.50MB  1.50%  google.golang.org/grpc/internal/grpcsync.NewEvent
       1MB     1% 93.50%        1MB     1%  runtime.malg
       1MB     1% 94.49%        1MB     1%  encoding/json.(*decodeState).literalStore

google.golang.org/grpc/internal/transport.newBufWriter 使用大量內(nèi)存真的很突出!這是泄漏與什么相關(guān)的第一個跡象:gRPC。查看我們的應(yīng)用程序源代碼,我們唯一使用 gRPC 的地方是 Google Cloud Secret Manager [6] :

?
1
2
3
4
client, err := secretmanager.NewClient(ctx)
if err != nil {
    return nil, fmt.Errorf("failed to create secretmanager client: %v", err)
}

在每個請求創(chuàng)建 client 時,我們沒有調(diào)用 client.Close() !所以,我添加了一個 Close 調(diào)用,問題就消失了:

defer client.Close()

我提交了修復(fù),然后 自動部署 [7] ,鋸齒立即消失了!

大約在同一時間,用戶在我們的 Cloud 的 Go 示例存儲庫中 [8] 提交了一個問題,其中包含 cloud.google.com 上 [9] 文檔的大部分 Go 示例。用戶注意到我們忘記調(diào)用 client.Close 了。

我曾多次看到同樣的事情出現(xiàn),所以我決定調(diào)查整個 repo。

我開始粗略估計(jì)有多少受影響的文件。使用 grep ,我們可以獲得包含 NewClient 樣式調(diào)用的所有文件的列表,然后將該列表傳遞給另一個調(diào)用 grep 以僅列出不包含 Close 的文件,同時忽略測試文件:

$ grep -L Close $(grep -El 'New[^(]*Client' **/*.go) | grep -v test

竟然有 207 個文件……就上下文而言,我們 .go 在 GoogleCloudPlatform/golang-samples [10] 存儲庫中有大約 1300 個文件。

考慮到問題的規(guī)模,我認(rèn)為一些自動化是 值得的 [11] 。我不想寫一個完整的 Go 程序來編輯文件,所以我使用 Bash:

$ grep -L Close $(grep -El 'New[^(]*Client' **/*.go) | grep -v test | xargs sed -i '/New[^(]*Client/,/}/s/}/}\ndefer client.Close()/'

它是完美的嗎?不。它對工作量有很大的影響嗎?是的!

第一部分(直到 test )與上面完全相同——獲取所有可能受影響的文件的列表(那些似乎創(chuàng)建了 Client 但從沒調(diào)用 Close 的文件)。

然后,我將該文件列表傳遞給 sed 進(jìn)行實(shí)際編輯。 xargs 調(diào)用你給它的命令,每一行都以 stdin 作為參數(shù)傳遞給給定的命令。

要理解該 sed 命令,查看 golang-samples repo 示例是什么樣子有助于理解(省略導(dǎo)入和客戶端初始化后的所有內(nèi)容):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// accessSecretVersion accesses the payload for the given secret version if one
// exists. The version can be a version number as a string (e.g. "5") or an
// alias (e.g. "latest").
func accessSecretVersion(w io.Writer, name string) error {
    // name := "projects/my-project/secrets/my-secret/versions/5"
    // name := "projects/my-project/secrets/my-secret/versions/latest"
    // Create the client.
    ctx := context.Background()
    client, err := secretmanager.NewClient(ctx)
    if err != nil {
        return fmt.Errorf("failed to create secretmanager client: %v", err)
    }
    // ...
}

在高層次上,我們初始化客戶端并檢查是否有錯誤。每當(dāng)你檢查錯誤時,都會有一個右花括號 ( } )。我使用這些信息來自動化編輯。

但是,該 sed 命令仍然很笨拙:

sed -i '/New[^(]*Client/,/}/s/}/}\ndefer client.Close()/'

-i 表示直接編輯文件。這不是問題,因?yàn)榇a用 git 管理了。

接下來,我使用 s 命令在檢查錯誤 defer client.Close() 后假定的右花括號 ( } )之后插入。

但是,我不想替換每個 } ,我只想要在 調(diào)用 NewClient 后 的 第一個 。要做到這一點(diǎn),你可以給一個 地址范圍 [12] 的 sed 搜索。

地址范圍可以包括在應(yīng)用接下來的任何命令之前要匹配的開始和結(jié)束模式。在這種情況下,開始是 /New[^(]*Client/ ,匹配 NewClient 類型調(diào)用,結(jié)束(由 a 分隔 , )是 /}/ ,匹配下一個大括號。這意味著我們的搜索和替換僅適用于調(diào)用 NewClient 和結(jié)束大括號之間!

通過了解上面的錯誤處理模式, if err != nil 條件的右大括號正是我們想要插入 Close 調(diào)用的位置。

一旦我自動編輯了所有示例文件,我用 goimports 開始修復(fù)格式。然后,我檢查了每個編輯過的文件,以確保它做了正確的事情:

  • 在服務(wù)器應(yīng)用程序中,我們應(yīng)該關(guān)閉客戶端,還是應(yīng)該保留它以備將來的請求使用?
  • 是 Client 實(shí)際的名字 client 還是別的什么?
  • 是否有一個以上的 Client 調(diào)用了 Close ?

完成后,只剩下 180 個已編輯的文件 [13] 。

最后一項(xiàng)工作是努力使其不再發(fā)生在用戶身上。我們想到了幾種方法:

  1. 更好的示例代碼;
  2. 更好的 GoDoc。我們更新了庫生成器,在生成庫時加上注釋,告知 client 需要調(diào)用 Close;
  3. 更好的庫。有沒有辦法可以自動 Close 客戶端?Finalizers?知道何能做得更好嗎?歡迎在 https://github.com/googleapis/google-cloud-go/issues/4498 上交流;

我希望你對 Go、內(nèi)存泄漏 pprof 、gRPC 和 Bash 有所了解。我很想聽聽你關(guān)于發(fā)現(xiàn)的內(nèi)存泄漏以及修復(fù)它們的方法的故事!如果你對我們?nèi)绾胃倪M(jìn)我們的 庫 [14] 或 示例 [15] 有任何想法,請通過提交 issue 告訴我們。

參考資料

[1]
Google Cloud Go 客戶端庫: https://github.com/googleapis/google-cloud-go

[2]
Cloud Run: https://cloud.google.com/run/docs/quickstarts/build-and-deploy/go

[3]
Go 服務(wù)器: https://github.com/googleapis/repo-automation-bots/tree/main/serverless-scheduler-proxy

[4]
pprof: https://pkg.go.dev/net/http/pprof

[5]
profiling Go 程序: https://go.dev/blog/pprof

[6]
Google Cloud Secret Manager: https://cloud.google.com/secret-manager/docs/quickstart

[7]
自動部署: https://cloud.google.com/build/docs/deploying-builds/deploy-cloud-run

[8]
Cloud 的 Go 示例存儲庫中: https://github.com/GoogleCloudPlatform/golang-samples

[9]
cloud.google.com 上: https://cloud.google.com/

[10]
GoogleCloudPlatform/golang-samples: https://github.com/GoogleCloudPlatform/golang-samples

[11]
值得的: https://xkcd.com/1205/

[12]
地址范圍: https://www.gnu.org/software/sed/manual/html_node/Addresses.html

[13]
180 個已編輯的文件: https://github.com/GoogleCloudPlatform/golang-samples/pull/2080

[14]
庫: https://github.com/googleapis/google-cloud-go

[15]
示例: https://github.com/GoogleCloudPlatform/golang-samples

到此這篇關(guān)于定位并修復(fù) Go 中的內(nèi)存泄露的文章就介紹到這了,更多相關(guān)定位Go內(nèi)存泄露內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://mp.weixin.qq.com/s/NEsoMtHElJwQ-n2CIH-Ogg

延伸 · 閱讀

精彩推薦
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggo日志系統(tǒng)logrus顯示文件和行號的操作

    go日志系統(tǒng)logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統(tǒng)logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • Golanggolang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • GolangGolang通脈之?dāng)?shù)據(jù)類型詳情

    Golang通脈之?dāng)?shù)據(jù)類型詳情

    這篇文章主要介紹了Golang通脈之?dāng)?shù)據(jù)類型,在編程語言中標(biāo)識符就是定義的具有某種意義的詞,比如變量名、常量名、函數(shù)名等等,Go語言中標(biāo)識符允許由...

    4272021-11-24
  • Golanggolang如何使用struct的tag屬性的詳細(xì)介紹

    golang如何使用struct的tag屬性的詳細(xì)介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細(xì)介紹,從例子說起,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網(wǎng)11352020-05-21
  • Golanggolang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關(guān)于golang的httpserver優(yōu)雅重啟的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,...

    helight2992020-05-14
  • GolangGolang中Bit數(shù)組的實(shí)現(xiàn)方式

    Golang中Bit數(shù)組的實(shí)現(xiàn)方式

    這篇文章主要介紹了Golang中Bit數(shù)組的實(shí)現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨(dú)尊11682021-06-09
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 成人免费毛片高清视频 | 精品久久久久国产 | 欧美日韩有码 | 日韩素人一区二区三区 | 中文字幕av第一页 | 色av综合| 亚洲黄色在线视频 | 欧美日韩国产一区二区三区 | 久久一精品 | 婷婷色av| 国产成人精品电影 | 亚洲免费中文字幕 | 久久精品免费 | 免费黄色在线看 | 欧美亚洲精品在线 | 男人天堂a | 欧美片网站免费 | 欧美黄视频| 国产91精品一区二区绿帽 | 欧美日韩国产一区二区三区 | 美女88av| 欧美精品 在线观看 | 成人精品在线观看 | 在线一区二区三区做爰视频网站 | 国产美女网站视频 | 一级免费毛片 | 国产一区二区三区四区在线观看 | 亚洲精品一区中文字幕乱码 | 狠狠ri | 欧美一区二区三区黄色 | www.成人.com| 欧美大片在线观看 | 玖玖国产 | 免费视频黄 | 亚洲最新无码中文字幕久久 | 成人在线免费观看视频 | 精品国产成人在线 | 欧美亚洲高清 | 蜜桃免费一区二区三区 | 国产成人精品免高潮在线观看 | the蜜臀av入口 |