1. 程式人生 > >golang程式效能分析

golang程式效能分析

最近在使用GraphQL編寫golang程式,但GraphQL框架在golang上的實踐比較少,很多效能上的資料也不夠全面。考慮到線上抗壓的問題,筆者決定對自己開發的服務模組進行效能壓測,評估下服務的整體效能。測試的工具鏈使用Vegeta+PPof+go-torch,PProf、go-torch上一篇文章Golang工具鏈 已經總結過不再贅述,vegeta會簡單講解下。

工具-vegeta

Vegeta是一個用Go語言編寫的多功能的HTTP負載測試工具,提供命令列工具和開發包。安裝見vegeta 說明。

Usage: vegeta [global flags] <command> [command flags]

global flags:
  -cpus int
使用CUP的數量 (預設 4 個) -profile string Enable profiling of [cpu, heap] -version 列印版本並退出 attack command: -body string 請求的主體檔案 -cert string TLS客戶PEM編碼的證書檔案 -connections int 沒個目標主機最大開啟閒置連結數 (預設 10000) -duration duration 持續攻擊時間 [0 = forever] -header value 請求頭 -insecure 忽略無效的伺服器TLS證書 -keepalive 使用持久連結 (default
true) -key string TLS客戶端PEM編碼的私鑰檔案 -laddr value 本地IP地址 (default 0.0.0.0) -lazy 延遲懶散的讀取目標 -output string 輸出檔案 (default "stdout") -rate uint 每秒請求數 (default 50) -redirects int 遵循重定向的次數. -1 不會遵循重定向但會標記為成功 (預設 10) -root-certs value TLS根證書檔案 (逗號分隔列表) -targets string
目標檔案 (default "stdin") -timeout duration 請求超時時間 (default 30s) -workers uint 初始化程序數 (default 10) report command: -inputs string 輸入檔案 (comma separated) (default "stdin") -output string 輸出檔案 (default "stdout") -reporter string 表報字元格式 [text, json, plot, hist[buckets]] (default "text") text 文字格式 json json格式 plot 在 Dygraphs 上生成一個可以互動式的HTML5基礎頁面 hist 計算並列印一個基於文字的直方圖 dump command: -dumper string Dumper [json, csv] (default "json") 指定轉儲格式 -inputs string Input files (comma separated) (default "stdin") 指定轉儲含有統計結果的輸入檔案,多個逗號分隔 -output string Output file (default "stdout") 指定把轉儲檔案寫入到輸出檔案 舉例: echo "GET http://localhost/" | vegeta attack -duration=5s | tee results.bin | vegeta report vegeta attack -targets=targets.txt > results.bin vegeta report -inputs=results.bin -reporter=json > metrics.json cat results.bin | vegeta report -reporter=plot > plot.html cat results.bin | vegeta report -reporter="hist[0,100ms,200ms,300ms]"

注意

  • -targets 指定一行分隔檔案中的攻擊目標,格式如下:

    • 簡單目標
    GET http://goku:9090/path/to/dragon?item=balls
    GET http://user:[email protected]:9090/path/to
    HEAD http://goku:9090/path/to/success
    • 自定義請求頭的目標
    GET http://user:[email protected]:9090/path/to
    X-Account-ID: 8675309
    
    DELETE http://goku:9090/path/to/remove
    Confirmation-Token: 90215
    Authorization: Token DEADBEEF
    • 自定義請求的主體
    POST http://goku:9090/things
    @/path/to/newthing.json
    
    PATCH http://goku:9090/thing/71988591
    @/path/to/thing-71988591.json
    • 自定義請求頭和請求主體
    POST http://goku:9090/things
    X-Account-ID: 99
    @/path/to/newthing.json
  • 進行負載測試時,不能因為Vegeta自身機器的效能瓶頸限制無法達到預期結果,例如開啟的檔案數、記憶體大小、CPU和網路頻寬,分散式的使用Vegeta是非常好的解決方案。

    • 為了確保開啟檔案描述和程序限制設定得高一些,可以在機器上使用ulimit命令
    • 可以使用pdsh分散式運維工具,對目標執行分散式攻擊。

程式效能分析過程

使用vegeta工具壓測http服務介面

命令

  • echo "POST http://192.168.168.189:8181/graphql" | vegeta attack -body /tmp/rqst/rqst.txt -duration=10s -rate=400 > r.bin

    • http://192.168.168.189:8181/graphql執行post請求,post的body放置在/tmp/rqst/rqst.txt目錄下,對目標地址進行持續10s的攻擊,每次傳送400個請求, 將請求的結果統計到r.bin檔案。

    • rqst.txt的內容如下,此處對部署在遠端GraphQL服務直接傳送Post請求,請求的操作名稱jobInfoList:

    {                                                                                                                                     
        "query":"query jobInfoList(  $projectId: String!){\n\tjobInfoList(projectId: $projectId) {\n    algorithmInfo {\n      algorithmId\n      descInfo\n      location\n      name\n      sourceFormat\n      status\n      targetFormat\n      version\n    }\n  }\n}",  
        "variables":{"projectId":"pr-testproject"},
        "operationName":"jobInfoList"
    }
    

結果

  • text格式

    /home# vegeta report -inputs=r.bin -reporter=text //檢視text格式分析資料,預設為該格式,可不寫
    Requests      [total, rate]            4000, 400.10
    Duration      [total, attack, wait]    10.0081328s, 9.997499937s, 10.632863ms
    Latencies     [mean, 50, 95, 99, max]  3.978566ms, 3.635816ms, 6.189994ms, 11.539779ms, 18.534089ms
    Bytes In      [total, mean]            148000, 37.00
    Bytes Out     [total, mean]            1380000, 345.00
    Success       [ratio]                  100.00%
    Status Codes  [code:count]             200:4000 
  • json格式

    /home# vegeta report -inputs=r.bin -reporter=json
    {
    "latencies:{"total":15914265193,"mean":3978566,"50th":3635816,"95th":6189994,"99th":11539779,"max":18534089},
    "bytes_in":{"total":148000,"mean":37},
    "bytes_out":{"total":1380000,"mean":345},
    "earliest":"2018-04-20T15:31:11.400851579+08:00","latest":"2018-04-20T15:31:21.398351516+08:00","end":"2018-04-20T15:31:21.408984379+08:00",
    "duration":9997499937,
    "wait":10632863,
    "requests":4000,
    "rate":400.10002752751205,
    "success":1,
    "status_codes":{"200":4000},
    "errors":null
    }

檢視生成的pprof分析的視覺化圖片

pprof常用作cpu、記憶體分析,需要使用pprof的場景

  • 定位記憶體洩露
  • 程式效率瓶頸
  • 檢視程式的呼叫圖

下面這張圖是CPU profile,可以清楚看到程式執行時的呼叫狀態,主要分析各個方法的耗時。下圖有些部分是虛線,是因為耗時比較少的節點沒有在圖上體現出來 ,但要把圖連起來,有的地方就使用虛線了。

這裡寫圖片描述

go-torch更直觀的分析工具

上面的圖在呼叫鏈比較簡單的情況下,會比較直觀,但在如此複雜的呼叫鏈下,確實看著有點亂,使用Uber開源的火焰圖絕對讓你開啟新世界的大門。

命令
  • 執行命令go-torch -u http://192.168.168.189:8181 -t 10

  • 執行命令後bash展示如下內容:

    INFO[14:16:19] Run pprof command: go tool pprof -raw -seconds 10 http://192.168.168.189:8181/debug/pprof/profile
    INFO[14:16:30] Writing svg to torch.svg

火焰圖展示

  • 下圖為使用go-torch生成的火焰圖,看起來比pprof分析的要更加直觀一些。

    這裡寫圖片描述

    火焰圖是具有互動性的:

    • 滑鼠懸浮: 火焰的每一層都會標註函式名,滑鼠懸浮時會顯示完整的函式名、抽樣抽中的次數、佔據總抽樣次數的百分比。

    這裡寫圖片描述

    • 點選放大:在某一層點選,火焰圖會水平放大,該層會佔據所有寬度,顯示詳細資訊。

    這裡寫圖片描述

    • 搜尋:按下Ctrl + F 會顯示一個搜尋框,可輸入關鍵字或正則表示式,所有符合條件的函式名會高亮顯示

    這裡寫圖片描述

總結

通過以上一系列分析,筆者定位到GraphQL的解析庫對程式的效能造成了一定的影響,這邊總結了幾種解決方式:

  • 用grpc替代GraphQL
  • http的Client和Server之間使用長連線進行通訊
  • 使用http2代替http1.1

參考資料