最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

線上golang grpc服務(wù)資源泄露問題排查

 更新時間:2023年12月28日 08:56:20   作者:liyunlong41  
這篇文章主要介紹了線上golang grpc服務(wù)資源泄露問題排查,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

前幾天告警群里報出一個go服務(wù)grpc接口出現(xiàn)很多超時現(xiàn)象,排查發(fā)現(xiàn)是服務(wù)有內(nèi)存泄露與cpu占用高的問題,在這里將排查的過程記錄一下,給大家提供排查問題的方向與思路,同時借鑒教訓(xùn),優(yōu)化自己服務(wù)代碼。

發(fā)現(xiàn)超時現(xiàn)象后,登錄機器看了下top,該服務(wù)總共有兩臺機器,發(fā)現(xiàn)02機器的cpu與內(nèi)存占用很高(如下圖第一個進程),而01機器都很低。

正常情況下不會有這么高的資源占用,可能是服務(wù)有資源泄露的問題,資源一致得不到釋放。

首先做的,是重啟服務(wù),優(yōu)先解決問題,資源泄露的問題可以通過重啟來快速解決,重啟后接口超時現(xiàn)象不再出現(xiàn),接口耗時恢復(fù)正常。

重啟后,開始排查問題,超時的服務(wù)是B服務(wù),上游有A服務(wù)調(diào)用B服務(wù),從A服務(wù)中找了幾個超時的請求,根據(jù)opentracing生成的tracer_id查詢?nèi)罩?,發(fā)現(xiàn)A服務(wù)調(diào)用B服務(wù)超時5s就返回錯誤了,B服務(wù)收到了A服務(wù)的請求,發(fā)現(xiàn)有兩種情況,一種是B立即收到了A的請求,但是處理了400+秒才返回;另一種是A發(fā)出請求400+秒后,B才開始處理請求。

另外發(fā)現(xiàn)grpc請求全部打到一臺機器上,另一臺機器沒什么量。

然后去看了下歷史cpu、內(nèi)存曲線,發(fā)現(xiàn)cpu在15分鐘內(nèi)上升至很高,同時內(nèi)存占用很高的現(xiàn)象。

拉長了內(nèi)存統(tǒng)計時間,發(fā)現(xiàn)A服務(wù)的內(nèi)存在緩慢增長,肯定是有內(nèi)存泄露的問題:

總結(jié)下觀察到的問題:

  • 請求為什么全部打到一臺機器上,兩臺機器的前面是有slb的,難道slb沒有發(fā)揮作用嗎?
  • cpu占用在15分鐘快速增長的原因是什么?
  • 請求處理時間慢的原因是什么?
  • 為什么會有內(nèi)存泄露現(xiàn)象出現(xiàn)?

Q1 slb沒有負載均衡

原因

針對Q1,特意去查了下slb的配置,由于slb是根據(jù)權(quán)重輪詢的,可能權(quán)重配置錯誤導(dǎo)致的請求分配不均,但是看了配置,slb的配置并沒有問題,兩臺后端服務(wù)器的權(quán)重相同。

然后去查了下,發(fā)現(xiàn)我們的slb是4層(tcp/udp層)的負載均衡,4層的slb是針對連接做負載均衡的,而不是針對請求,當(dāng)連接的客戶端很少時,負載也可能不均衡,4層的負載均衡是客戶端和服務(wù)器tcp直連。

然后去兩臺機器上netstat看了下,發(fā)現(xiàn)01機器沒有A服務(wù)的連接,02機器有A服務(wù)的連接。同時grpc維持的是長連接并且復(fù)用連接,對于新請求不會新建連接,這樣在第一次經(jīng)過slb的負載均衡創(chuàng)建連接后,grpc的請求會復(fù)用這個連接,請求會全部打到連接的機器上。

如何解決?

知道原因了,那么如何解決呢?可以通過etcd與grpc兩者結(jié)合來實現(xiàn)服務(wù)注冊與服務(wù)發(fā)現(xiàn),grpc客戶端根據(jù)所有server的ip來實現(xiàn)負載均衡。

Q2 cpu快速增長

原因

對于Q2,由于當(dāng)時服務(wù)沒有設(shè)置pprof,無法看到運行的狀況……后面加了pprof,又不能馬上復(fù)現(xiàn),所以暫時是通過看代碼的方式來猜測哪些地方可能出了問題- -

想到之前的請求處理了400+秒,并且當(dāng)時內(nèi)存占用很高,代碼中又有worker類的任務(wù),每秒從數(shù)據(jù)庫中取出數(shù)據(jù),對每條數(shù)據(jù)啟動一個goroutine處理。

偽代碼如下:

for {
	datas := Mysql.GetDatas()
	for _, v := range datas {
		cur := v
		go func() {
			handle(cur)
		}
	}
	time.Sleep(time.Second)
}

正常情況下,這是沒問題的,但是當(dāng)時機器的內(nèi)存占用接近100%,那么goroutine的處理時間肯定變長,如果處理時間超過1秒甚至遠超一秒,那么這個goroutine還沒處理完,worker又新起了一個goroutine,goroutine的數(shù)量沒有控制,多了以后又占用更多資源,舊的goroutine處理時間更長,worker還是每秒啟動一個新的goroutine……后面就產(chǎn)生了goroutine泄露,這可能是導(dǎo)致cpu增長的主要原因。

所以初步猜測是內(nèi)存泄露問題,導(dǎo)致內(nèi)存占用很高,然后導(dǎo)致goroutine處理時間過長,又導(dǎo)致goroutine泄露,goroutine進一步導(dǎo)致cpu、內(nèi)存增長。

后來在線上加了pprof,但是內(nèi)存泄露比較緩慢,需要等一段時間才能捕獲,到時候在這里補充。

如何解決?

對于goroutine泄露的解決,自然是控制goroutine的數(shù)量,我把偽代碼改成了如下來控制goroutine(判斷超過限制數(shù)量就sleep):

int32 runningG = 0
const maxRunningG = 200
for {
	if atomic.LoadInt32(&runningG) > maxRunningG {
		time.Sleep(time.Seconds * 3)
		continue
	}
	datas := Mysql.GetDatas()
	for _, v := range datas {
		cur := v
		atomic.AddInt32(&runningG, 1)
		go func() {
			handle(cur)
			atomic.AddInt32(&runningG, -1)
		}
	}
	time.Sleep(time.Second)
}

Q3 請求處理緩慢

原因

處理請求緩慢的原因可能是Q2 goroutine泄露問題導(dǎo)致的cpu占用過高,請求處理不過來了。

如何解決?

參考Q2解決方案

Q4 內(nèi)存泄露問題

原因1

同樣也是直接從代碼的角度排查,借鑒了網(wǎng)上一些人的內(nèi)存泄露經(jīng)驗,發(fā)現(xiàn)一個方法中對于http請求的處理方式可能有問題。

對于每個http請求,該方法每次都會新建一個http.Client與transport, 偽代碼如下。

...
tr := &http.Transport{
   TLSClientConfig: &tls.Config{
      ... // 證書相關(guān)
   },
}
client := &http.Client{Transport: tr}
response, err := client.Post(url, contentType, body)
if err != nil {
   return
}
responseByte, err := ioutil.ReadAll(response.Body)
if err != nil {
   return
}
...

而通過http.Client與transport的注釋我們可以看出這兩個是可以復(fù)用的。

http.Client:

// The Client's Transport typically has internal state (cached TCP
// connections), so Clients should be reused instead of created as
// needed. Clients are safe for concurrent use by multiple goroutines.
//

http.Transport:

// Transports should be reused instead of created as needed.
// Transports are safe for concurrent use by multiple goroutines.

且該方法對于http.Response.Body沒有調(diào)用**Close()**方法,這可能導(dǎo)致潛在的資源泄露。

如何解決?

創(chuàng)建全局的http Client和Transport并且設(shè)置好超時時間等參數(shù),復(fù)用這個client。http請求返回的response需要調(diào)用http.Response.Body.Close()釋放連接使其可以被其他協(xié)程復(fù)用。

原因2

同時發(fā)現(xiàn)對于mysql查詢結(jié)果的處理,也可能有些問題,偽代碼如下:

rows, err := db.Query(sql, case)
if err != nil {
	return
}
for rows.Next() {
	if err = rows.Scan(...); err != nil{
		return
	}
	...
}

對于sql.Query的結(jié)果rows,如果沒有rows.Close(),但是rows.Scan()讀取了所有的數(shù)據(jù),那么rows的資源會自動得到釋放;

但是如果Scan發(fā)生錯誤,rows沒有讀取完,又沒有rows.Close(),就可能導(dǎo)致潛在的內(nèi)存泄露。

如何解決?

每次都調(diào)用rows.Close()方法來釋放資源。

rows, err := db.Query(sql, case)
if err != nil {
	return
}
defer rows.Close() //
for rows.Next() {
	if err = rows.Scan(...); err != nil{
		return
	}
	...
}

其他原因

通過pprof正在分析中…… 待補充。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解析Go 標準庫 http.FileServer 實現(xiàn)靜態(tài)文件服務(wù)

    解析Go 標準庫 http.FileServer 實現(xiàn)靜態(tài)文件服務(wù)

    http.FileServer 方法屬于標準庫 net/http,返回一個使用 FileSystem 接口 root 提供文件訪問服務(wù)的 HTTP 處理器。下面通過本文給大家介紹Go 標準庫 http.FileServer 實現(xiàn)靜態(tài)文件服務(wù)的相關(guān)知識,感興趣的朋友一起看看吧
    2018-08-08
  • 純Go語言實現(xiàn)百萬級數(shù)據(jù)高效導(dǎo)出Excel和CSV實戰(zhàn)指南

    純Go語言實現(xiàn)百萬級數(shù)據(jù)高效導(dǎo)出Excel和CSV實戰(zhàn)指南

    這篇文章主要為大家詳細介紹了如何通過純Go語言實現(xiàn)百萬級數(shù)據(jù)高效導(dǎo)出Excel和CSV的相關(guān)知識,文中的示例代碼講解詳細,需要的小伙伴可以了解下
    2025-09-09
  • Golang利用compress/flate包來壓縮和解壓數(shù)據(jù)

    Golang利用compress/flate包來壓縮和解壓數(shù)據(jù)

    在處理需要高效存儲和快速傳輸?shù)臄?shù)據(jù)時,數(shù)據(jù)壓縮成為了一項不可或缺的技術(shù),Go語言的compress/flate包為我們提供了對DEFLATE壓縮格式的原生支持,本文將深入探討compress/flate包的使用方法,揭示如何利用它來壓縮和解壓數(shù)據(jù),并提供實際的代碼示例,需要的朋友可以參考下
    2024-08-08
  • Go語言極簡入門之語法、并發(fā)及避坑全攻略

    Go語言極簡入門之語法、并發(fā)及避坑全攻略

    Go是由谷歌支持的開源編程語言,屬于編譯型語言,對并發(fā)編程有較好的支持,這篇文章主要介紹了Go語言極簡入門之語法、并發(fā)及避坑的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2026-06-06
  • Golang中基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)

    Golang中基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)

    HTTP協(xié)議是基于TCP/IP協(xié)議棧的,并且它也是一個面向普通文本的協(xié)議。這篇文章主要詳細介紹了Golang中基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù),感興趣的小伙伴可以借鑒一下
    2023-04-04
  • Go接口的用法詳解

    Go接口的用法詳解

    本文主要介紹了Go接口的用法詳解,包括定義接口、實現(xiàn)接口、使用接口、空接口等,通過接口,可以實現(xiàn)多態(tài)性,即一個對象可以實現(xiàn)多個接口,從而實現(xiàn)不同接口的行為,感興趣的可以了解一下
    2023-11-11
  • golang?debug調(diào)試的實現(xiàn)

    golang?debug調(diào)試的實現(xiàn)

    本文主要介紹了使用Go語言進行本地調(diào)試和遠程調(diào)試,文中通過圖文介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • linux中用shell快速安裝配置Go語言的開發(fā)環(huán)境

    linux中用shell快速安裝配置Go語言的開發(fā)環(huán)境

    相信每位開發(fā)者都知道選擇一門開發(fā)語言,免不了需要安裝配置開發(fā)環(huán)境,所以這篇文章給大家分享了linux下使用shell一鍵安裝配置GO語言開發(fā)環(huán)境的方法,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-10-10
  • GoLang的sync.WaitGroup與sync.Once簡單使用講解

    GoLang的sync.WaitGroup與sync.Once簡單使用講解

    sync.WaitGroup類型,它比通道更加適合實現(xiàn)這種一對多的goroutine協(xié)作流程。WaitGroup是開箱即用的,也是并發(fā)安全的。同時,與之前提到的同步工具一樣,它一旦被真正的使用就不能被復(fù)制了
    2023-01-01
  • golang操作mongodb的方法

    golang操作mongodb的方法

    這篇文章主要介紹了golang操作mongodb的方法,涉及Go語言操作mongodb的連接、讀取及顯示的方法,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02

最新評論

沙洋县| 和平区| 顺平县| 伊宁市| 广昌县| 色达县| 康乐县| 历史| 深泽县| 威海市| 抚州市| 武义县| 友谊县| 高阳县| 留坝县| 仪征市| 法库县| 定日县| 宣城市| 视频| 色达县| 文成县| 屏东市| 屏东市| 和硕县| 聊城市| 康定县| 乐东| 汪清县| 临武县| 花莲市| 白朗县| 汉源县| 千阳县| 司法| 延吉市| 城口县| 华安县| 太仆寺旗| 永昌县| 永吉县|