溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

victoriaMetrics代理性能優(yōu)化問(wèn)題怎么解決

發(fā)布時(shí)間:2022-04-06 10:17:14 來(lái)源:億速云 閱讀:329 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹了victoriaMetrics代理性能優(yōu)化問(wèn)題怎么解決的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇victoriaMetrics代理性能優(yōu)化問(wèn)題怎么解決文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

起因

最近有做一個(gè)Prometheus metrics代理的一個(gè)小項(xiàng)目,暫稱為prom-proxy,目的是為了解析特定的指標(biāo)(如容器、traefik、istio等指標(biāo)),然后在原始指標(biāo)中加入應(yīng)用ID(當(dāng)然還有其他指標(biāo)操作,暫且不表)。經(jīng)過(guò)簡(jiǎn)單的本地驗(yàn)證,就發(fā)布到聯(lián)調(diào)環(huán)境,跑了幾個(gè)禮拜一切正常,以為相安無(wú)事。但自以為沒(méi)事不代表真的沒(méi)事。

昨天突然老環(huán)境和新上prom-proxy的環(huán)境都出現(xiàn)了數(shù)據(jù)丟失的情況,如下圖:

victoriaMetrics代理性能優(yōu)化問(wèn)題怎么解決

prom-proxy有一個(gè)自服務(wù)指標(biāo)request_total,經(jīng)觀察發(fā)現(xiàn),該指標(biāo)增長(zhǎng)極慢,因而一開(kāi)始懷疑是發(fā)送端的問(wèn)題(這是一個(gè)誤區(qū),后面會(huì)講為何要增加緩存功能)。

進(jìn)一步排查,發(fā)現(xiàn)上游發(fā)送端(使用的是victoriaMetrics的vmagent組件)出現(xiàn)了如下錯(cuò)誤,說(shuō)明是prom-proxy消費(fèi)的數(shù)據(jù)跟不上vmagent產(chǎn)生的數(shù)據(jù):

2022-03-24T09:55:49.945Z        warn    VictoriaMetrics/app/vmagent/remotewrite/client.go:277   couldn't send a block with size 370113 bytes to "1:secret-url": Post "xxxx": context deadline exceeded (Client.Timeout exceeded while awaiting headers); re-sending the block in 16.000 seconds

出現(xiàn)這種問(wèn)題,首先想到的是增加并發(fā)處理功能。當(dāng)前的并發(fā)處理數(shù)為8(即后臺(tái)的goroutine數(shù)目),考慮到線上宿主機(jī)的core有30+,因此直接將并發(fā)處理數(shù)拉到30。經(jīng)驗(yàn)證發(fā)現(xiàn)毫無(wú)改善。

另外想到的一種方式是緩存,如使用kafka或使用golang自帶的緩存chan。但使用緩存也有問(wèn)題,如果下游消費(fèi)能力一直跟不上,緩存中將會(huì)產(chǎn)生大量積壓的數(shù)據(jù),且Prometheus監(jiān)控指標(biāo)具有時(shí)效性,積壓過(guò)久的數(shù)據(jù),可用性并不高又浪費(fèi)存儲(chǔ)空間。

下面是使用了緩存chan的例子,s.reqChan的初始大小設(shè)置為5000,并使用cacheTotal指標(biāo)觀察緩存的變更。這種方式下,數(shù)據(jù)接收和處理變?yōu)榱水惒?但并不完全異步)。

上面一開(kāi)始有講到使用request_total查看上游的請(qǐng)求是個(gè)誤區(qū),是因?yàn)檎?qǐng)求統(tǒng)計(jì)和請(qǐng)求處理是同步的,因此如果請(qǐng)求沒(méi)有處理完,就無(wú)法接收下一個(gè)請(qǐng)求,request_total也就無(wú)法增加。

func (s *Server) injectLabels(w http.ResponseWriter, r *http.Request) {
    data, _ := DecodeWriteRequest(r.Body)
    s.reqChan <- data
    cacheTotal.Inc()
    w.WriteHeader(http.StatusNoContent)
}
func (s *Server) Start() {
    go func() {
        for data := range s.reqChan {
            cacheTotal.Dec()
            processor := s.pool.GetWorkRequest()
            go func() {
                processor.JobChan <- data
                res := <-processor.RetChan
                if 0 != len(res.errStr) {
                    log.Errorf("err msg:%s,err.code:%d", res.errStr, res.statusCode)
                    return
                }
            }()
        }
    }()
}

上線后觀察發(fā)現(xiàn)cacheTotal的統(tǒng)計(jì)增加很快,說(shuō)明之前就是因?yàn)樘幚砟芰Σ蛔銓?dǎo)致request_total統(tǒng)計(jì)慢。

至此似乎陷入了一個(gè)死胡同。多goroutine和緩存都是不可取的。

回顧一下,prom-proxy中處理了cadvisor、kube-state-metrics、istio和traefik的指標(biāo),同時(shí)在處理的時(shí)候做了自監(jiān)控,統(tǒng)計(jì)了各個(gè)類型的指標(biāo)。例如:

prom-proxy_metrics_total{kind="container"} 1.0396728e+07
prom-proxy_metrics_total{kind="istio"} 620414
prom-proxy_metrics_total{kind="total"} 2.6840415e+07

cacheTotal迅猛增加的同時(shí),發(fā)現(xiàn)request_total增長(zhǎng)極慢(表示已處理的請(qǐng)求),且istio類型的指標(biāo)處理速率很慢,,而container類型的指標(biāo)處理速度則非常快。這是一個(gè)疑點(diǎn)。

vmagent的一個(gè)請(qǐng)求中可能包含上千個(gè)指標(biāo),可能會(huì)混合各類指標(biāo),如容器指標(biāo)、網(wǎng)關(guān)指標(biāo)、中間件指標(biāo)等等。

通過(guò)排查istio指標(biāo)處理的相關(guān)代碼,發(fā)現(xiàn)有三處可以優(yōu)化:

  • 更精確地匹配需要處理的指標(biāo):之前是通過(guò)前綴通配符匹配的,經(jīng)過(guò)精確匹配之后,相比之前處理的指標(biāo)數(shù)下降了一半。

  • 代碼中有重復(fù)寫(xiě)入指標(biāo)的bug:這一處IO操作耗時(shí)極大

  • 將寫(xiě)入指標(biāo)操作放到獨(dú)立的goroutine pool中,獨(dú)立于標(biāo)簽處理

經(jīng)過(guò)上述優(yōu)化,上線后發(fā)現(xiàn)緩存為0,性能達(dá)標(biāo)!

一開(kāi)始在開(kāi)發(fā)完prom-proxy之后也做了簡(jiǎn)單的benchmark測(cè)試,但考慮到是在辦公網(wǎng)驗(yàn)證的,網(wǎng)速本來(lái)就慢,因此注釋掉了寫(xiě)入指標(biāo)的代碼,初步驗(yàn)證性能還算可以就結(jié)束了,沒(méi)想到埋了一個(gè)深坑。

所以所有功能都需要覆蓋驗(yàn)證,未驗(yàn)證的功能點(diǎn)都有可能是坑!

總結(jié)

  • 服務(wù)中必須增加必要的自監(jiān)控指標(biāo):對(duì)于高頻率請(qǐng)求的服務(wù),增加請(qǐng)求緩存機(jī)制,即便不能削峰填谷,也可以作為一個(gè)監(jiān)控指標(biāo)(通過(guò)Prometheus metric暴露的),用于觀察是否有請(qǐng)求積壓;此外由于很多線上環(huán)境并不能直接到宿主機(jī)進(jìn)行操作,像獲取火焰圖之類的方式往往不可行,此時(shí)指標(biāo)就可以作為一個(gè)參考模型。

  • 進(jìn)行多維度度、全面的benchmark:代碼性能分為計(jì)算型和IO型。前者是算法問(wèn)題,后者則涉及的問(wèn)題比較多,如網(wǎng)絡(luò)問(wèn)題、并發(fā)不足的問(wèn)題、使用了阻塞IO等。在進(jìn)行benchmark的時(shí)候可以將其分開(kāi)驗(yàn)證,即注釋掉可能耗時(shí)的IO操作,首先驗(yàn)證計(jì)算型的性能,在計(jì)算型性能達(dá)標(biāo)時(shí)啟用IO操作,進(jìn)一步做全面的benchmark驗(yàn)證。

后續(xù)

喜聞樂(lè)見(jiàn)的后續(xù)來(lái)了。。。

由于公司有兩個(gè)大的線上集群,暫稱為more集群和less集群,很不幸,性能達(dá)標(biāo)的就是less集群的,其指標(biāo)數(shù)據(jù)相比more集群來(lái)說(shuō)非常less,大概是前者的十分之一。上到more集群之后服務(wù)內(nèi)存直接達(dá)到50G,多個(gè)副本一起吃內(nèi)存,直接將節(jié)點(diǎn)搞掛了。

迫不得已(又是那句話,感覺(jué)對(duì)了的點(diǎn)往往不對(duì)),重新做了pprof壓力測(cè)試,發(fā)現(xiàn)內(nèi)存黑洞就是下面這個(gè)函數(shù)(來(lái)自Prometheus),即便在辦公電腦下進(jìn)行壓測(cè),其內(nèi)存使用仍然達(dá)到好幾百M(fèi)。該函數(shù)主要是讀取vmagent傳來(lái)的請(qǐng)求,首先進(jìn)行snappy.Decode解碼,然后unmarshal到臨時(shí)變量wr中。低流量下完全沒(méi)有問(wèn)題,但高流量下完全無(wú)法應(yīng)對(duì):

func DecodeWriteRequest(r io.Reader) (*ReqData, error) {
	compressed, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	reqBuf, err := snappy.Decode(nil, compressed)
	if err != nil {
		return nil, err
	}
	var wr prompb.WriteRequest
	if err := proto.Unmarshal(reqBuf, &wr); err != nil {
		return nil, err
	}
	return &ReqData{
		reqBuf: reqBuf,
		wr:     &wr,
	}, nil
}

解決辦法就是拿出sync.pool大殺器,下面方式參考了victoriaMetrics的byteutil庫(kù)(代碼路徑lib/byteutil),有興趣的可以去看下,經(jīng)過(guò)壓測(cè),相同測(cè)試情況下內(nèi)存降到了不足100M。

func DecodeWriteRequest(r io.Reader, callback func(*prompb.WriteRequest)) error {
	ctx := getPushCtx(r)
	defer putPushCtx(ctx)
	if err := ctx.Read(); err != nil {
		return err
	}
	bb := bodyBufferPool.Get()
	defer bodyBufferPool.Put(bb)
	var err error
	bb.B, err = snappy.Decode(bb.B[:cap(bb.B)], ctx.reqBuf.B)
	if err != nil {
		return err
	}
	wr := getWriteRequest()
	defer putWriteRequest(wr)
	if err := wr.Unmarshal(bb.B); err != nil {
		return err
	}
	callback(wr)
	return nil
}

這樣一來(lái)性能完全達(dá)標(biāo),10core下單pod每秒可以處理250w個(gè)指標(biāo)!

重新發(fā)布線上,自然又出問(wèn)題了,這次prom-proxy服務(wù)一切正常,但導(dǎo)致后端vmstorage(victoriametrics的存儲(chǔ)服務(wù))內(nèi)存爆滿。經(jīng)過(guò)初步定位,是由于出現(xiàn)了slow insert,即出現(xiàn)大量 active time series導(dǎo)致緩存miss,進(jìn)而導(dǎo)致內(nèi)存暴增(prom-proxy服務(wù)會(huì)在原始指標(biāo)中增加標(biāo)簽,并創(chuàng)建其他新的指標(biāo),這兩類指標(biāo)數(shù)目非常龐大,都屬于active time series)。

victoriaMetrics代理性能優(yōu)化問(wèn)題怎么解決

victoriaMetrics代理性能優(yōu)化問(wèn)題怎么解決

最終的解決方式是將修改的指標(biāo)作分類,并支持配置化啟用,即如果修改的指標(biāo)類型有:A、B、C、D四類。首先上線A,然后上線B,以此類推,讓vmstorage逐步處理active time series,以此減少對(duì)后端存儲(chǔ)的瞬時(shí)壓力。

vmstorage有一個(gè)參數(shù):--storage.maxDailySeries,它可以限制active time series的數(shù)目。但環(huán)境中正常情況下就有大量active time serials,如果設(shè)置了這個(gè)參數(shù),新增的active time serials極有可能會(huì)擠掉老的active time serials,造成老數(shù)據(jù)丟失。

關(guān)于“victoriaMetrics代理性能優(yōu)化問(wèn)題怎么解決”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“victoriaMetrics代理性能優(yōu)化問(wèn)題怎么解決”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI