Golang pprof 效能調優實戰,效能提升 3 倍!
1.需求背景
服務升級,需要對kafka訊息持久化服務進行壓測,預計每分鐘要產生訊息400w條。
目前使用Golang實現了批量傳送kafka訊息的介面,但100w條訊息就要還是50s多,無法滿足需求,因此需要對傳送kafka介面進行效能調優
2.問題分析
2.1傳送的訊息量是否已經達到了網路io的瓶頸
經過測試,本地除錯時,確實存在這個問題。
在伺服器上除錯,則可以避免這個問題
2.2傳送kafka介面的實現存在效能問題
- 組裝kafka訊息的邏輯(BenchMark測試也沒啥問題)
// 使用jsonpath替換某個欄位
// source {"name":{"first":"Janet","last":"Prichard"}}
// jsonKeyValue {"name.first": {"name.first", "_", "name.last"}}
// want {"name":{"first":"Janet_Prichard","last":"Prichard"}}
func replaceJsonMsg(source string, jsonKeyValue map[string][]string) string {
for keyPath, valueList := range jsonKeyValue {
var builder strings.Builder
for _, valuePath := range valueList {
value := gjson.Get(source, valuePath)
if value.Exists() {
builder.WriteString(value.Str)
} else {
builder.WriteString(valuePath)
}
}
source, _ = sjson.Set(source, keyPath, builder.String())
}
return source
}
rikasai@huacainoMBP handlers % go test -bench=BenchmarkReplaceJsonMsg -benchtime=5s -cpuprofile jsonCpu.out
goos: darwin
goarch: amd64
pkg: BigDataTestTool/handlers
BenchmarkReplaceJsonMsg-12 7363956 814 ns/op
PASS
啟動web ui檢視效能分析圖
rikasai@huacainoMBP handlers % go tool pprof -http=:8081 jsonCpu.out
- 傳送kafka的第三方庫有問題(經過測試,直接發訊息,不經過任何處理時,第三方庫能滿足需求)
2.3kafka服務端配置存在問題
- 經研發大佬確認過,配置正常
3.除錯過程
上述的幾種猜測都沒發現什麼大問題
我想replaceJsonMsg
這個函式用的是string
替換,改成map
會不會更快
於是就有了replaceMapMsg
func replaceMapMsg(msg *map[string]interface{}, jsonKeyValue *map[string][]string) *[]byte {
for keyPath, valueList := range *jsonKeyValue {
var builder strings.Builder
for _, valuePath := range valueList {
property, err := GetProperty(*msg, valuePath)
if err == nil {
s := fmt.Sprintf("%v", property)
builder.WriteString(s)
} else {
s := fmt.Sprintf("%v", valuePath)
builder.WriteString(s)
}
}
UpdateProperty(*msg, keyPath, builder.String())
}
b, _ := json.Marshal(msg)
return &b
}
func BenchmarkReplaceMapMsg(b *testing.B) {
msg := map[string]interface{}{"name": map[string]interface{}{"first": "Janet", "last": "Prichard"}}
j := map[string][]string{"name.first": {"name.first", "_", "name.last"}}
for i := 0; i < b.N; i++ {
ReplaceMapMsg(&msg, &j)
}
}
結果大吃一驚!
反向優化了!!!
rikasai@huacainoMBP handlers % go test -bench=BenchmarkReplaceMapMsg -benchtime=5s -cpuprofile mapCpu.out
goos: darwin
goarch: amd64
pkg: BigDataTestTool/handlers
BenchmarkReplaceMapMsg-12 41468 419347 ns/op
PASS
ok BigDataTestTool/handlers 19.071s
rikasai@huacainoMBP handlers % go tool pprof -http=:8080 mapCpu.out
Serving web UI on http://localhost:8080
趕緊看一波pprof
結果
一語驚醒夢中人~
json.Marshal
原來是這個傢伙佔了大頭
通過map替換是很快,但終究還是要序列化成[]byte
型別才能傳送kafka訊息
那用replaceJsonMsg
是事先序列化好了,怎麼還會那麼慢呢?
for begin <= end {
deviceNo := fmt.Sprintf("%v%v", b.DevicePrefix, begin)
for _, m := range b.MsgPayload {
var msg string
if b.MsgType == "json" {
m["deviceNo"] = deviceNo
marshal, _ := json.Marshal(m)
s := string(marshal)
msg = replaceJsonMsg(s, b.JsonKeyValue)
}
p.Produce(&kafka.Message{
TopicPartition: kafka.TopicPartition{Topic: &t, Partition: kafka.PartitionAny},
Value: []byte(msg),
}, nil)
}
if (end-begin)*int64(len(b.MsgPayload))%100000 == 0 {
p.Flush(5 * 1000)
}
begin++
}
好傢伙,每個迴圈都執行一次json.Marshal(m)
, 發100w條訊息就會執行100w次,簡直要命啊!
4.優化結果
// 先序列化,下面用到的時候,只需要迭代strMsg這個切片取出即可
strMsg := make([]string, 0, len(b.MsgPayload)) // 定義切片要小心,要先指定好容量,否則append會觸發自動擴容
if b.MsgType == "json" {
for _, m := range b.MsgPayload {
marshal, _ := json.Marshal(m)
s := string(marshal)
strMsg = append(strMsg, s)
}
}
for begin <= end {
deviceNo := fmt.Sprintf("%v%v", b.DevicePrefix, begin)
var msg string
if b.MsgType == "json" {
for _, s := range strMsg {
msg = replaceJsonMsg(s, deviceNo, b.JsonKeyValue)
p.Produce(&kafka.Message{
TopicPartition: kafka.TopicPartition{Topic: &t, Partition: kafka.PartitionAny},
Value: []byte(msg),
}, nil)
}
}
優化前傳送100w條訊息,耗時將近56s
{
"success_count": 1000001,
"fail_count": 0,
"parse_fail": [],
"elapsed": "55.874803s",
"msg": "success"
}
優化後傳送100w條訊息,耗時將近18s,提升了三倍的效能!
距離每分鐘400w還差一點點,還得繼續加油~
{
"success_count": 1000001,
"fail_count": 0,
"parse_fail": [],
"elapsed": "17.823006273s",
"msg": "success"
}
相關文章
- golang 效能調優分析工具 pprof(下)Golang
- golang 效能調優分析工具 pprof (上)Golang
- 效能調優實戰
- golang 效能優化分析:benchmark 結合 pprofGolang優化
- JVM效能調優與實戰篇JVM
- Golang 大殺器之效能剖析 PProfGolang
- 3倍+提升,高德地圖極致效能優化之路地圖優化
- Spring Boot Serverless 實戰系列 | 效能調優Spring BootServer
- 查詢效能提升3倍!Apache Hudi 查詢優化了解下?Apache優化
- GTest(基於YApi)介面研發效能提升10倍 實戰API
- 高效能 Java 計算服務的效能調優實戰Java
- TiDB 效能分析&效能調優&優化實踐大全TiDB優化
- nginx引數調優能提升多少效能Nginx
- golang 使用pprof和go-torch做效能分析Golang
- 記一次提升18倍的效能優化優化
- JVM效能調優與實戰進階篇-上JVM
- 小程式redux效能優化,提升三倍渲染速度Redux優化
- 效能分析工具 - pprof
- 個推技術實踐 | Spark效能調優看這篇,效能提升60%↑ 成本降低50%↓Spark
- Hive效能調優實踐 - VidhyaHive
- 【效能調優】效能測試、分析與調優基礎
- ElasticSearch效能調優Elasticsearch
- adnroid效能調優
- JVM效能調優與實戰基礎理論篇-下JVM
- TiDB 效能分析&效能調優&最佳化實踐大全TiDB
- 多場景下 3-11 倍效能提升,Apache Doris 1.2 新版本效能揭秘!Apache
- mpvue效能優化實戰技巧Vue優化
- Linux效能優化實戰(一)Linux優化
- Linux效能優化實戰(二)Linux優化
- 實時計算Flink效能調優
- Spark 效能調優--資源調優Spark
- Go藉助PProf的一次效能優化Go優化
- Java效能調優實戰-劉超-極客時間-返現優惠Java
- Golang net/http 效能優化GolangHTTP優化
- Golang效能分析與優化Golang優化
- Linux之效能調優Linux
- 效能監控調優
- linux調優效能命令Linux