日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

IO 密集型服务 性能优化实战记录

發(fā)布時(shí)間:2024/4/11 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 IO 密集型服务 性能优化实战记录 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

背景

項(xiàng)目背景

Feature 服務(wù)作為特征服務(wù),產(chǎn)出特征數(shù)據(jù)供上游業(yè)務(wù)使用。服務(wù)壓力:高峰期 API 模塊 10wQPS,計(jì)算模塊 20wQPS。服務(wù)本地緩存機(jī)制:

  • 計(jì)算模塊有本地緩存,且命中率較高,最高可達(dá) 50% 左右;

  • 計(jì)算模塊本地緩存在每分鐘第 0 秒會(huì)全部失效,而在此時(shí)流量會(huì)全部擊穿至下游 Codis;

  • Codis 中 Key 名 = 特征名 + 地理格子 Id + 分鐘級(jí)時(shí)間串;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Feature 服務(wù)模塊圖

面對(duì)問(wèn)題

服務(wù) API 側(cè)存在較嚴(yán)重的 P99 耗時(shí)毛刺問(wèn)題(固定出現(xiàn)在每分鐘第 0-10s),導(dǎo)致上游服務(wù)的訪問(wèn)錯(cuò)誤率達(dá)到 1‰ 以上,影響到業(yè)務(wù)指標(biāo);目標(biāo):解決耗時(shí)毛刺問(wèn)題,將 P99 耗時(shí)整體優(yōu)化至 15ms 以下;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?API 模塊返回上游 P99 耗時(shí)圖

解決方案

服務(wù) CPU 優(yōu)化

背景

偶然的一次上線變動(dòng)中,發(fā)現(xiàn)對(duì) Feature 服務(wù)來(lái)說(shuō) CPU 的使用率的高低會(huì)較大程度上影響到服務(wù)耗時(shí),因此從提高服務(wù) CPU Idle 角度入手,對(duì)服務(wù)耗時(shí)毛刺問(wèn)題展開優(yōu)化。

優(yōu)化

通過(guò)對(duì) Pprof profile 圖的觀察發(fā)現(xiàn) JSON 反序列化操作占用了較大比例(50% 以上),因此通過(guò)減少反序列化操作、更換 JSON 序列化庫(kù)(json-iterator)兩種方式進(jìn)行了優(yōu)化。

效果

收益:CPU idle 提升 5%,P99 耗時(shí)毛刺從 30ms 降低至 20 ms 以下。

優(yōu)化后的耗時(shí)曲線(紅色與綠色線)

關(guān)于 CPU 與耗時(shí)

為什么 CPU Idle 提升耗時(shí)會(huì)下降

  • 反序列化時(shí)的開銷減少,使單個(gè)請(qǐng)求中的計(jì)算時(shí)間得到了減少;

  • 單個(gè)請(qǐng)求的處理時(shí)間減少,使同時(shí)并發(fā)處理的請(qǐng)求數(shù)得到了減少,減輕了調(diào)度切換、協(xié)程/線程排隊(duì)、資源競(jìng)爭(zhēng)的開銷;

關(guān)于 json-iterator 庫(kù)

json-iterator 庫(kù)為什么快標(biāo)準(zhǔn)庫(kù) json 庫(kù)使用 reflect.Value 進(jìn)行取值與賦值,但 reflect.Value 不是一個(gè)可復(fù)用的反射對(duì)象,每次都需要按照變量生成 reflect.Value 結(jié)構(gòu)體,因此性能很差。json-iterator 實(shí)現(xiàn)原理是用 reflect.Type 得出的類型信息通過(guò)「對(duì)象指針地址+字段偏移」的方式直接進(jìn)行取值與賦值,而不依賴于 reflect.Value,reflect.Type 是一個(gè)可復(fù)用的對(duì)象,同一類型的 reflect.Type 是相等的,因此可按照類型對(duì) reflect.Type 進(jìn)行 cache 復(fù)用??偟膩?lái)說(shuō)其作用是減少內(nèi)存分配反射調(diào)用次數(shù),進(jìn)而減少了內(nèi)存分配帶來(lái)的系統(tǒng)調(diào)用、鎖和 GC 等代價(jià),以及使用反射帶來(lái)的開銷。

詳情可見:https://cloud.tencent.com/developer/article/1064753

調(diào)用方式優(yōu)化 - 對(duì)沖請(qǐng)求

背景

  • Feature 服務(wù) API 模塊訪問(wèn)計(jì)算模塊 P99 顯著高于 P95;API 模塊訪問(wèn)計(jì)算模塊 P99 與 P95 耗時(shí)曲線

  • 經(jīng)觀察計(jì)算模塊不同機(jī)器之間毛刺出現(xiàn)時(shí)間點(diǎn)不同,單機(jī)毛刺呈偶發(fā)現(xiàn)象,所有機(jī)器聚合看呈規(guī)律性毛刺;計(jì)算模塊返回 API P99 耗時(shí)曲線(未聚合)計(jì)算模塊返回 API P99 耗時(shí)曲線(均值聚合)

  • 優(yōu)化

  • 針對(duì) P99 高于 P95 現(xiàn)象,提出對(duì)沖請(qǐng)求方案,對(duì)毛刺問(wèn)題進(jìn)行優(yōu)化;

  • 對(duì)沖請(qǐng)求:把對(duì)下游的一次請(qǐng)求拆成兩個(gè),先發(fā)第一個(gè),n毫秒超時(shí)后,發(fā)出第二個(gè),兩個(gè)請(qǐng)求哪個(gè)先返回用哪個(gè);Hedged requests. A simple way to curb latency variability is to issue the same request to multiple replicas and use the results from whichever replica responds first. We term such requests “hedged requests” because a client first sends one request to the replica be- lieved to be the most appropriate, but then falls back on sending a secondary request after some brief delay. The cli- ent cancels remaining outstanding re- quests once the first result is received. Although naive implementations of this technique typically add unaccept- able additional load, many variations exist that give most of the latency-re- duction effects while increasing load only modestly. One such approach is to defer send- ing a secondary request until the first request has been outstanding for more than the 95th-percentile expected la- tency for this class of requests. This approach limits the additional load to approximately 5% while substantially shortening the latency tail. The tech- nique works because the source of la- tency is often not inherent in the par- ticular request but rather due to other forms of interference. 摘自:論文《The Tail at Scale》

  • 調(diào)研

    • 閱讀論文 Google《The Tail at Scale》;

    • 開源實(shí)現(xiàn):BRPC、RPCX;

    • 工業(yè)實(shí)踐:百度默認(rèn)開啟、Grab LBS 服務(wù)(下游純內(nèi)存型數(shù)據(jù)庫(kù))效果非常明顯、谷歌論文中也有相關(guān)的實(shí)踐效果闡述;

    落地實(shí)現(xiàn):修改自 RPCX 開源實(shí)現(xiàn)

    package?backuprequestimport?("sync/atomic""time""golang.org/x/net/context" )var?inflight?int64//?call?represents?an?active?RPC. type?call?struct?{Name??stringReply?interface{}?//?The?reply?from?the?function?(*struct).Error?error???????//?After?completion,?the?error?status.Done??chan?*call??//?Strobes?when?call?is?complete. }func?(call?*call)?done()?{select?{case?call.Done?<-?call:default:logger.Debug("rpc:?discarding?Call?reply?due?to?insufficient?Done?chan?capacity")} }func?BackupRequest(backupTimeout?time.Duration,?fn?func()?(interface{},?error))?(interface{},?error)?{ctx,?cancelFn?:=?context.WithCancel(context.Background())defer?cancelFn()callCh?:=?make(chan?*call,?2)call1?:=?&call{Done:?callCh,?Name:?"first"}call2?:=?&call{Done:?callCh,?Name:?"second"}go?func(c?*call)?{defer?helpers.PanicRecover()c.Reply,?c.Error?=?fn()c.done()}(call1)t?:=?time.NewTimer(backupTimeout)select?{case?<-ctx.Done():?//?cancel?by?contextreturn?nil,?ctx.Err()case?c?:=?<-callCh:t.Stop()return?c.Reply,?c.Errorcase?<-t.C:go?func(c?*call)?{defer?helpers.PanicRecover()defer?atomic.AddInt64(&inflight,?-1)if?atomic.AddInt64(&inflight,?1)?>?BackupLimit?{metric.Counter("backup",?map[string]string{"mark":?"limited"})return}metric.Counter("backup",?map[string]string{"mark":?"trigger"})c.Reply,?c.Error?=?fn()c.done()}(call2)}select?{case?<-ctx.Done():?//?cancel?by?contextreturn?nil,?ctx.Err()case?c?:=?<-callCh:metric.Counter("backup_back",?map[string]string{"call":?c.Name})return?c.Reply,?c.Error} }

    效果

    收益:P99 耗時(shí)整體從 20-60ms 降低至 6ms,毛刺全部干掉;(backupTimeout=5ms)

    API 模塊返回上游服務(wù)耗時(shí)統(tǒng)計(jì)圖

    《The Tail at Scale》論文節(jié)選及解讀

    括號(hào)中內(nèi)容為個(gè)人解讀為什么存在變異性?(高尾部延遲的響應(yīng)時(shí)間)

    • 導(dǎo)致服務(wù)的個(gè)別部分出現(xiàn)高尾部延遲的響應(yīng)時(shí)間的變異性(耗時(shí)長(zhǎng)尾的原因)可能由于許多原因而產(chǎn)生,包括:

    • 共享的資源。機(jī)器可能被不同的應(yīng)用程序共享,爭(zhēng)奪共享資源(如CPU核心、處理器緩存、內(nèi)存帶寬和網(wǎng)絡(luò)帶寬)(在云上環(huán)境中這個(gè)問(wèn)題更甚,如不同容器資源爭(zhēng)搶、Sidecar 進(jìn)程影響);在同一個(gè)應(yīng)用程序中,不同的請(qǐng)求可能爭(zhēng)奪資源。

    • 守護(hù)程序。后臺(tái)守護(hù)程序可能平均只使用有限的資源,但在安排時(shí)可能產(chǎn)生幾毫秒的中斷。

    • 全局資源共享。在不同機(jī)器上運(yùn)行的應(yīng)用程序可能會(huì)爭(zhēng)奪全球資源(如網(wǎng)絡(luò)交換機(jī)和共享文件系統(tǒng)(數(shù)據(jù)庫(kù)))。

    • 維護(hù)活動(dòng)。后臺(tái)活動(dòng)(如分布式文件系統(tǒng)中的數(shù)據(jù)重建,BigTable等存儲(chǔ)系統(tǒng)中的定期日志壓縮(此處指 LSM Compaction 機(jī)制,基于 RocksDB 的數(shù)據(jù)庫(kù)皆有此問(wèn)題),以及垃圾收集語(yǔ)言中的定期垃圾收集(自身和上下游都會(huì)有 GC 問(wèn)題 1. Codis proxy 為 GO 語(yǔ)言所寫,也會(huì)有 GC 問(wèn)題;2. 此次 Feature 服務(wù)耗時(shí)毛刺即時(shí)因?yàn)榉?wù)本身 GC 問(wèn)題,詳情見下文)會(huì)導(dǎo)致周期性的延遲高峰;以及排隊(duì)。中間服務(wù)器和網(wǎng)絡(luò)交換機(jī)的多層排隊(duì)放大了這種變化性。

    減少組件的可變性

    • 后臺(tái)任務(wù)可以產(chǎn)生巨大的CPU、磁盤或網(wǎng)絡(luò)負(fù)載;例子是面向日志的存儲(chǔ)系統(tǒng)的日志壓縮和垃圾收集語(yǔ)言的垃圾收集器活動(dòng)。

    • 通過(guò)節(jié)流、將重量級(jí)的操作分解成較小的操作(例如 GO、Redis rehash 時(shí)漸進(jìn)式搬遷),并在整體負(fù)載較低的時(shí)候觸發(fā)這些操作(例如某數(shù)據(jù)庫(kù)將 RocksDB Compaction 操作放在凌晨定時(shí)執(zhí)行),通常能夠減少后臺(tái)活動(dòng)對(duì)交互式請(qǐng)求延遲的影響。

    關(guān)于消除變異源

    • 消除大規(guī)模系統(tǒng)中所有的延遲變異源是不現(xiàn)實(shí)的,特別是在共享環(huán)境中。

    • 使用一種類似于容錯(cuò)計(jì)算的方法(此處指對(duì)沖請(qǐng)求),容尾軟件技術(shù)從不太可預(yù)測(cè)的部分中形成一個(gè)可預(yù)測(cè)的整體(對(duì)下游耗時(shí)曲線進(jìn)行建模,從概率的角度進(jìn)行優(yōu)化)。

    • 一個(gè)真實(shí)的谷歌服務(wù)的測(cè)量結(jié)果,該服務(wù)在邏輯上與這個(gè)理想化的場(chǎng)景相似;根服務(wù)器通過(guò)中間服務(wù)器將一個(gè)請(qǐng)求分發(fā)到大量的葉子服務(wù)器。該表顯示了大扇出對(duì)延遲分布的影響。在根服務(wù)器上測(cè)量的單個(gè)隨機(jī)請(qǐng)求完成的第99個(gè)百分點(diǎn)的延遲是10ms。然而,所有請(qǐng)求完成的第99百分位數(shù)延遲是140ms,95%的請(qǐng)求完成的第99百分位數(shù)延遲是70ms,這意味著等待最慢的5%的請(qǐng)求完成的時(shí)間占總的99%百分位數(shù)延遲的一半。專注于這些慢速異常值的技術(shù)可以使整體服務(wù)性能大幅降低。

    • 同樣,由于消除所有的變異性來(lái)源也是不可行的,因此正在為大規(guī)模服務(wù)開發(fā)尾部容忍技術(shù)。盡管解決特定的延遲變異來(lái)源的方法是有用的,但最強(qiáng)大的尾部容錯(cuò)技術(shù)可以重新解決延遲問(wèn)題,而不考慮根本原因。這些尾部容忍技術(shù)允許設(shè)計(jì)者繼續(xù)為普通情況進(jìn)行優(yōu)化,同時(shí)提供對(duì)非普通情況的恢復(fù)能力。

    對(duì)沖請(qǐng)求原理

    對(duì)沖請(qǐng)求典型場(chǎng)景
    • 其原理是從概率角度出發(fā),利用下游服務(wù)的耗時(shí)模型,在這條耗時(shí)曲線上任意取兩個(gè)點(diǎn),其中一個(gè)小于x的概率,這個(gè)概率遠(yuǎn)遠(yuǎn)大于任意取一個(gè)點(diǎn)小于x的概率,所以可以極大程度降低耗時(shí);

    • 但如果多發(fā)的請(qǐng)求太多了,比如說(shuō)1倍,會(huì)導(dǎo)致下游壓力劇增,耗時(shí)曲線模型產(chǎn)生惡化,達(dá)不到預(yù)期的效果,如果控制比如說(shuō)在5%以內(nèi),下游耗時(shí)曲線既不會(huì)惡化,也可以利用他95分位之前的那個(gè)平滑曲線,因此對(duì)沖請(qǐng)求超時(shí)時(shí)間的選擇也是一個(gè)需要關(guān)注的點(diǎn);

    • 當(dāng)超過(guò)95分位耗時(shí)的時(shí)候,再多發(fā)一個(gè)請(qǐng)求,這時(shí)候這整個(gè)請(qǐng)求剩余的耗時(shí)就取決于在這整個(gè)線上任取一點(diǎn),和在95分位之后的那個(gè)線上任取一點(diǎn),耗時(shí)是這兩點(diǎn)中小的那個(gè),從概率的角度看,這樣95分位之后的耗時(shí)曲線,會(huì)比之前平滑相當(dāng)多;

    • 這個(gè)取舍相當(dāng)巧妙,只多發(fā)5%的請(qǐng)求,就能基本上直接干掉長(zhǎng)尾情況;

    • 局限性

      • 如同一個(gè) mget 接口查 100 個(gè) key 與查 10000 個(gè) key 耗時(shí)一定差異很大,這種情況下對(duì)沖請(qǐng)求時(shí)無(wú)能為力的,因此需要保證同一個(gè)接口請(qǐng)求之間質(zhì)量是相似的情況下,這樣下游的耗時(shí)因素就不取決于請(qǐng)求內(nèi)容本身;

      • 如 Feature 服務(wù)計(jì)算模塊訪問(wèn) Codis 緩存擊穿導(dǎo)致的耗時(shí)毛刺問(wèn)題,在這種情況下對(duì)沖請(qǐng)求也無(wú)能為力,甚至一定情況下會(huì)惡化耗時(shí);

      • 請(qǐng)求需要冪等,否則會(huì)造成數(shù)據(jù)不一致;

      • 總得來(lái)說(shuō)對(duì)沖請(qǐng)求是從概率的角度消除偶發(fā)因素的影響,從而解決長(zhǎng)尾問(wèn)題,因此需要考量耗時(shí)是否為業(yè)務(wù)側(cè)自身固定因素導(dǎo)致,舉例如下:

      • 對(duì)沖請(qǐng)求超時(shí)時(shí)間并非動(dòng)態(tài)調(diào)整而是人為設(shè)定,因此極端情況下會(huì)有雪崩風(fēng)險(xiǎn),解決方案見一下小節(jié);

    名稱來(lái)源 backup request 好像是 BRPC 落地時(shí)候起的名字,論文原文里叫 Hedged requests,簡(jiǎn)單翻譯過(guò)來(lái)是對(duì)沖請(qǐng)求,GRPC 也使用的這個(gè)名字。

    關(guān)于雪崩風(fēng)險(xiǎn)

    對(duì)沖請(qǐng)求超時(shí)時(shí)間并非動(dòng)態(tài)調(diào)整,而是人為設(shè)定,因此極端情況下會(huì)有雪崩風(fēng)險(xiǎn);

    摘自《Google SRE》 如果不加限制確實(shí)會(huì)有雪崩風(fēng)險(xiǎn),有如下解法

    • BRPC 實(shí)踐:對(duì)沖請(qǐng)求會(huì)消耗一次對(duì)下游的重試次數(shù);

    • bilibili 實(shí)踐:

      • 對(duì) retry 請(qǐng)求下游會(huì)阻斷級(jí)聯(lián);

      • 本身要做熔斷;

      • 在 middleware 層實(shí)現(xiàn)窗口統(tǒng)計(jì),限制重試總請(qǐng)求占比,比如 1.1 倍;

    • 服務(wù)自身對(duì)下游實(shí)現(xiàn)熔斷機(jī)制,下游服務(wù)對(duì)上游流量有限流機(jī)制,保證不被打垮。從兩方面出發(fā)保證服務(wù)的穩(wěn)定性;

    • Feature 服務(wù)實(shí)踐:對(duì)每個(gè)對(duì)沖請(qǐng)求在發(fā)出和返回時(shí)增加 atmoic 自增自減操作,如果大于某個(gè)值(請(qǐng)求耗時(shí) ?? QPS ?? 5%),則不發(fā)出對(duì)沖請(qǐng)求,從控制并發(fā)請(qǐng)求數(shù)的角度進(jìn)行流量限制;

    語(yǔ)言 GC 優(yōu)化

    背景

    在引入對(duì)沖請(qǐng)求機(jī)制進(jìn)行優(yōu)化后,在耗時(shí)方面取得了突破性的進(jìn)展,但為從根本上解決耗時(shí)毛刺,優(yōu)化服務(wù)內(nèi)部問(wèn)題,達(dá)到標(biāo)本兼治的目的,著手對(duì)服務(wù)的耗時(shí)毛刺問(wèn)題進(jìn)行最后的優(yōu)化;

    優(yōu)化

    第一步:觀察現(xiàn)象,初步定位原因對(duì) Feature 服務(wù)早高峰毛刺時(shí)的 Trace 圖進(jìn)行耗時(shí)分析后發(fā)現(xiàn),在毛刺期間程序 GC pause 時(shí)間(GC 周期與任務(wù)生命周期重疊的總和)長(zhǎng)達(dá)近 50+ms(見左圖),絕大多數(shù) goroutine 在 GC 時(shí)進(jìn)行了長(zhǎng)時(shí)間的輔助標(biāo)記(mark assist,見右圖中淺綠色部分),GC 問(wèn)題嚴(yán)重,因此懷疑耗時(shí)毛刺問(wèn)題是由 GC 導(dǎo)致;

    第二步:從原因出發(fā),進(jìn)行針對(duì)性分析

    • 根據(jù)觀察計(jì)算模塊服務(wù)平均每 10 秒發(fā)生 2 次 GC,GC 頻率較低,但在每分鐘前 10s 第一次與第二次的 GC 壓力大小(做 mark assist 的 goroutine 數(shù))呈明顯差距,因此懷疑是在每分鐘前 10s 進(jìn)行第一次 GC 時(shí)的壓力過(guò)高導(dǎo)致了耗時(shí)毛刺。

    • 根據(jù) Golang GC 原理分析可知,G 被招募去做輔助標(biāo)記是因?yàn)樵?G 分配堆內(nèi)存太快導(dǎo)致,而 計(jì)算模塊每分鐘緩存失效機(jī)制會(huì)導(dǎo)致大量的下游訪問(wèn),從而引入更多的對(duì)象分配,兩者結(jié)合互相印證了為何在每分鐘前 10s 的第一次 GC 壓力超乎尋常;

    關(guān)于 GC 輔助標(biāo)記 mark assist 為了保證在Marking過(guò)程中,其它G分配堆內(nèi)存太快,導(dǎo)致Mark跟不上Allocate的速度,還需要其它G配合做一部分標(biāo)記的工作,這部分工作叫輔助標(biāo)記(mutator assists)。在Marking期間,每次G分配內(nèi)存都會(huì)更新它的”負(fù)債指數(shù)”(gcAssistBytes),分配得越快,gcAssistBytes越大,這個(gè)指數(shù)乘以全局的”負(fù)載匯率”(assistWorkPerByte),就得到這個(gè)G需要幫忙Marking的內(nèi)存大小(這個(gè)計(jì)算過(guò)程叫revise),也就是它在本次分配的mutator assists工作量(gcAssistAlloc)。引用自:https://wudaijun.com/2020/01/go-gc-keypoint-and-monitor/

    第三步:按照分析結(jié)論,設(shè)計(jì)優(yōu)化操作從減少對(duì)象分配數(shù)角度出發(fā),對(duì) Pprof heap 圖進(jìn)行觀察

    • 在 inuse_objects 指標(biāo)下 cache 庫(kù)占用最大;

    • 在 alloc_objects 指標(biāo)下 json 序列化占用最大;

    但無(wú)法確定哪一個(gè)是真正使分配內(nèi)存增大的因素,因此著手對(duì)這兩點(diǎn)進(jìn)行分開優(yōu)化;

    通過(guò)對(duì)業(yè)界開源的 json 和 cache 庫(kù)調(diào)研后(調(diào)研報(bào)告:https://segmentfault.com/a/1190000041591284),采用性能較好、低分配的 GJSON 和 0GC 的 BigCache 對(duì)原有庫(kù)進(jìn)行替換;

    效果

    • 更換 JSON 序列化庫(kù) GJSON 庫(kù)優(yōu)化無(wú)效果;

    • 更換 Cache 庫(kù) BigCache 庫(kù)效果明顯,inuse_objects 由 200-300w 下降到 12w,毛刺基本消失;

    計(jì)算模塊耗時(shí)統(tǒng)計(jì)圖(淺色部分:GJSON,深色部分:BigCache)API 模塊返回上游耗時(shí)統(tǒng)計(jì)圖

    關(guān)于 Golang GC

    在通俗意義上常認(rèn)為,GO GC 觸發(fā)時(shí)機(jī)為堆大小增長(zhǎng)為上次 GC 兩倍時(shí)。但在 GO GC 實(shí)際實(shí)踐中會(huì)按照 Pacer 調(diào)頻算法根據(jù)堆增長(zhǎng)速度、對(duì)象標(biāo)記速度等因素進(jìn)行預(yù)計(jì)算,使堆大小在達(dá)到兩倍大小前提前發(fā)起 GC,最佳情況下會(huì)只占用 25% CPU 且在堆大小增長(zhǎng)為兩倍時(shí),剛好完成 GC。

    關(guān)于 Pacer 調(diào)頻算法:https://golang.design/under-the-hood/zh-cn/part2runtime/ch08gc/pacing/

    但 Pacer 只能在穩(wěn)態(tài)情況下控制 CPU 占用為 25%,一旦服務(wù)內(nèi)部有瞬態(tài)情況,例如定時(shí)任務(wù)、緩存失效等等,Pacer 基于穩(wěn)態(tài)的預(yù)判失效,導(dǎo)致 GC 標(biāo)記速度小于分配速度,為達(dá)到 GC 回收目標(biāo)(在堆大小到達(dá)兩倍之前完成 GC),會(huì)導(dǎo)致大量 Goroutine 被招募去執(zhí)行 Mark Assist 操作以協(xié)助回收工作,從而阻礙到 Goroutine 正常的工作執(zhí)行。因此目前 GO GC 的 Marking 階段對(duì)耗時(shí)影響時(shí)最為嚴(yán)重的。

    關(guān)于 gc pacer 調(diào)頻器

    引用自:https://go.googlesource.com/proposal/+/a216b56e743c5b6b300b3ef1673ee62684b5b63b/design/44167-gc-pacer-redesign.md

    最終效果

    API 模塊 P99 耗時(shí)從 20-50ms 降低至 6ms,訪問(wèn)錯(cuò)誤率從 1‰ 降低到 1?。

    API 返回上游服務(wù)耗時(shí)統(tǒng)計(jì)圖

    總結(jié)

  • 當(dāng)分析耗時(shí)問(wèn)題時(shí),觀察監(jiān)控或日志后,可能會(huì)發(fā)現(xiàn)趨勢(shì)完全匹配的兩種指標(biāo),誤以為是因果關(guān)系,但卻有可能這兩者都是外部表現(xiàn),共同受到第三變量的影響,相關(guān)但不是因果

  • 相對(duì)于百毫秒耗時(shí)服務(wù),低延時(shí)服務(wù)的耗時(shí)會(huì)較大程度上受到 CPU 使用率的影響,在做性能優(yōu)化時(shí)切勿忽視這點(diǎn);(線程排隊(duì)、調(diào)度損耗、資源競(jìng)爭(zhēng)等)

  • 對(duì)于高并發(fā)、低延時(shí)服務(wù),耗時(shí)方面受到下游的影響可能只是一個(gè)方面,服務(wù)自身開銷如序列化、GC 等都可能會(huì)較大程度上影響到服務(wù)耗時(shí);

  • 性能優(yōu)化因從提高可觀測(cè)性入手,如鏈路追蹤、標(biāo)準(zhǔn)化的 Metric、go pprof 工具等等,打好排查基礎(chǔ),基于多方可靠數(shù)據(jù)進(jìn)行分析與猜測(cè),最后著手進(jìn)行優(yōu)化、驗(yàn)證,避免盲人摸象似的操作,妄圖通過(guò)碰運(yùn)氣的方式解決問(wèn)題;

  • 了解一些簡(jiǎn)單的建模知識(shí)對(duì)耗時(shí)優(yōu)化的分析與猜測(cè)階段會(huì)有不錯(cuò)的幫助;

  • 理論結(jié)合實(shí)際問(wèn)題進(jìn)行思考;多看文章、參與分享、進(jìn)行交流,了解更多技術(shù),擴(kuò)展視野;每一次的討論和質(zhì)疑都是進(jìn)一步深入思考的機(jī)會(huì),以上多項(xiàng)優(yōu)化都出自與大佬(特別鳴謝 @李心宇@劉琦@龔勛)的討論后的實(shí)踐;

  • 同為性能優(yōu)化,耗時(shí)優(yōu)化不同于 CPU、內(nèi)存等資源優(yōu)化,更加復(fù)雜,難度較高,在做資源優(yōu)化時(shí) Go 語(yǔ)言自帶了方便易用的 PProf 工具,可以提供很大的幫助,但耗時(shí)優(yōu)化尤其是長(zhǎng)尾問(wèn)題的優(yōu)化非常艱難,因此在進(jìn)行優(yōu)化的過(guò)程中一定要穩(wěn)住心態(tài)、耐心觀察,行百里者半九十。

  • 關(guān)注請(qǐng)求之間共享資源的爭(zhēng)用導(dǎo)致的耗時(shí)問(wèn)題,不僅限于下游服務(wù),服務(wù)自身的 CPU、內(nèi)存(引發(fā) GC)等也是共享資源的一部分;

  • 總結(jié)

    以上是生活随笔為你收集整理的IO 密集型服务 性能优化实战记录的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。