文章目錄
使用 ghz 來對 gRPC 做負載測試
最近興起想要比較幾個 gRPC load test 工具的使用心得,這才發現過去在建立 gRPC service 時因為專案時間壓力並沒有特別紀錄 ghz 的用法,後來工作也慢慢以 DevOps 為主,剛好趁這個機會順便複習一下
ghz 是一套用 go 撰寫的 command line 工具,主要是用於進行 load test 與 benchmark gRPC 服務
基本環境說明
- macOS Monterey 12.3.1
- ghz v0.106.1
ASP.NET Core gRPC server default project template
greet.proto
syntax = "proto3"; option csharp_namespace = "grpc6"; package greet; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply); } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings. message HelloReply { string message = 1; }
streaming 部份參考之前筆記 C# 搭配 gRPC 中使用 stream RPC
greet.proto
service CandidateService { rpc DownloadCv (DownloadByName) returns (stream Candidate); rpc CreateCv (stream Candidate) returns (CreateCvResponse); rpc CreateDownloadCv (stream Candidate) returns (stream Candidates); } message Candidates { repeated Candidate Candidates = 2; } message Candidate { string Name = 1; repeated Job Jobs = 2; } message Job { string Title = 1; int32 Salary = 2; string JobDescription = 3; } message DownloadByName { string Name = 1; } message CreateCvResponse { bool IsSuccess = 1; }CandidateService
public class CandidateService:grpc6.CandidateService.CandidateServiceBase { public override async Task<CreateCvResponse> CreateCv(IAsyncStreamReader<Candidate> requestStream, ServerCallContext context) { var result = new CreateCvResponse { IsSuccess = false }; // stream 讀取 while (await requestStream.MoveNext()) { var candidate = requestStream.Current; // 實際處理 Console.WriteLine(candidate.Name); } return result; } public override async Task DownloadCv(DownloadByName request, IServerStreamWriter<Candidate> responseStream, ServerCallContext context) { var fakeJobs = new Faker<Job>() .RuleFor(a => a.Title, (f, u) => f.Company.Bs()) .RuleFor(a => a.Salary, (f, u) => f.Commerce.Random.Int(1000, 2000)) .RuleFor(a => a.JobDescription, (f, u) => f.Lorem.Text()); var createRequests = new Faker<Candidate>() .RuleFor(a => a.Name, (f, u) => f.Name.FullName()) .RuleFor(a => a.Jobs, (f, u) => { u.Jobs.AddRange(fakeJobs.GenerateBetween(3, 5)); return u.Jobs; }).Generate(); // 將每筆資料逐一透過 WriteAsync 輸出 await responseStream.WriteAsync(createRequests); } public override async Task CreateDownloadCv(IAsyncStreamReader<Candidate> requestStream, IServerStreamWriter<Candidates> responseStream, ServerCallContext context) { var candidates = new Candidates(); // 將收到的資料逐一取出 while (await requestStream.MoveNext()) { var candidate = requestStream.Current; candidates.Candidates_.Add(candidate); // 將處理後的資料回傳 await responseStream.WriteAsync(candidates); } } }
下載與安裝
擇一即可
下載
可以至 ghz:GitHub releases page 下載所需環境 os 的壓縮檔後解壓縮使用
mac 可以使用 hoembrew 安裝
brew install ghz
使用方式
語法
ghz [<flags>] [<host>]範例
ghz --insecure --proto ./grpc6/Protos/greet.proto --call greet.Greeter.SayHello -d '{"name":"Yowko"}' 0.0.0.0:5143<flags>參數說明--config- 指定
JSON或是TOML格式的檔案位置 - config 檔案的設定值會與 cli 參數整併且 cli 參數優先權較高 (cli 參數會覆寫 config 檔案設定值)
- 指定
--proto- 指定定義 service 的
.proto檔案位置 - 若未指定
--proto與--protoset則會嘗試透過 server reflection 來取得可用 service
- 指定定義 service 的
--protoset- 指定編譯過的
.protoset檔案位置 - 若未指定
--proto與--protoset則會嘗試透過 server reflection 來取得可用 service
- 指定編譯過的
--call?- 呼叫完全符合規則的方法:格式為
package.Service/Methodorpackage.Service.Method
- 呼叫完全符合規則的方法:格式為
-i,--import-paths- 用來指定 import proto 的路徑
- 可以使用
,來指定多組路徑 - 目前所在資料夾與使用
--proto指定的 proto 檔所在資料夾都會自動加入 import 清單中
--cacert- 指定可用來驗證 server 的可信任根憑證位置
- ghz 預設會使用系統的預設根憑證來建立安全連線
- 加上
--skipTLS可以略過 TLS 檢查
--cert- 指定 client 憑證 (公鑰) 的位置
- 必需同時使用
--key
--key- 指定 client 私鑰的位置
- 必需同時使用
--cert
--cname- 驗證 TLS 憑證時覆寫 server 名稱
--skipTLS- 略過 server 憑證鏈與主機名稱的 TLS client 驗證
--insecure- 使用明碼與不安全連線
--authority- 會將值做為
:authoritypseudo-header - 只在使用
--insecure有用
- 會將值做為
--async- 儘快使 request 做非同步
-r,--rps- 所有 RPS (requests per second) 的速率限制
- 預設值為
0(無限制) - 所有 RPS 會依照指定的 concurrency 選項分送給 worker
--load-schedule- 指定 load test 排程模式
- 允許設定值:
const、step與line - 預設值為:
const - 使用
const時以q選項來指定 RPS (我看說明文件沒有關係qoption 的內容) - 使用
step時以load-start,load-step,load-end,load-step-duration, 與load-max-duration來決定階段性增減的細節 - 使用
line時以load-start,load-step,load-end,load-step-duration, 與load-max-duration來線性增減,但line就是線性增減,原則上就是透過load-step指定斜率且load-step-duration就是1s
--load-start- 指定 load test 開始的 RPS
- 適用於
step或line模式
--load-step- 在
step模式下指定 load test 遞增的 RPS 值 - 在
line模式下指定 load test 斜率
- 在
--load-end- 指定 load test 終止的 RPS (可不提供)
- 適用於
step或line模式 load-end與load-max-duration以先達成的優先
--load-max-duration- 指定 load test 最長持續時間(可不提供)
- 以
--load-end的 RPS 數量執行這個時間設定的 load test load-end與load-max-duration以先達成的優先
-c,--concurrency- 指定同時執行的 worker 數
- 適用於
const並發排程模式
--concurrency-schedule- 指定 load test 並發排程模式
- 允許設定值:
const、step與line - 預設值為:
const
--concurrency-start- 指定 load test 開始的並發 RPS
- 適用於
step或line並發模式
--concurrency-end- 指定 load test 終止的並發 RPS
- 適用於
step或line並發模式
--concurrency-step=1- 在
step並發模式下指定 load test 遞增的 RPS 值 - 在
line並發模式下指定 load test 斜率
- 在
--concurrency-step-duration- 指定 load test 並發 step 持續時間
- 適用於
step並發模式
--concurrency-max-duration- 指定 load test 並發最長持續時間
- 適用於
step或line並發模式
-n,--total- 指定總測試的 request 數量
- 預設為
200 - benchmark 主要就是
-n挑配-c取得的
-t,--timeout- 指定每個 request timeout 的時間
- 預設為
20s 0表示永不 timeout
-z,--duration- 指定發出 request 的持續時間
- 如果設定這個值,
-n的設定就會被忽略
-x,--max-duration- 不忽略
-n設定的最長執行時間 - 如果在
-n(最大測試總量) 完成前,達成這個設求,程式依然會停止
- 不忽略
--duration-stop- 指定
-z(duration) 已滿足的情況下,如何處理已發送但仍未完成的 request - 允許設定值:
close、wait與ignore close:立即關閉連線,可能會出現錯誤:transport is closingwait:會等待 request 完成,但仍需遵守-t設定ignore:會立即關閉連線,但報告不會將這些 request 計入
- 指定
-d,--data- 以 stringified JSON 做為呼叫的資料
- 如果 value 中有
@則以stdin讀取內容 - 一元請求(unary requests) 允許
一個(重複用) 或是陣列(循環用) 訊息 - client streaming or bi-directional streaming 則僅允許 json 陣列 (就算只提供一個 json 也會轉為只有一個元素的陣列)
-D,--data-file- 提供呼叫資料的 json 路徑
-b,--binary- 呼叫資料可以從
stdin讀取並序列化為 protocol buffer 訊息
- 呼叫資料可以從
-B,--binary-file- 指定序列化過的呼叫資料檔案路徑
-m,--metadata- stringified JSON 的 request metadat
-M,--metadata-file- metadata 的檔案路徑
--stream-interval- streaming 間隔時間
- 僅適用於
client streamingorbi-directional streaming
--stream-call-duration- 最大的 stream 呼叫持續時間
client streamingorbi-directional streaming會持續發送至時間到server streaming會持續接受到時間到 (會出現 canceled error)
--stream-call-count- 設定 streaming 最大的發送訊息數量 (
client streamingorbi-directional streaming)或接收訊數量 (server streaming) - 若 call data 大於指定數量,只會取部份使用;若資料小於指定數量則會重複使用
- 設定 streaming 最大的發送訊息數量 (
--stream-dynamic-messages- 允許重態建立 streaming 訊息內容
--reflect-metadata- 將 metadata 反射為 stringified JSON
- 僅用於 reflection 的 request
--max-recv-message-size- 指定最大允許 client 接收的訊息大小
- 可以使用
bytes設定,或是42 MB這類 human readable value
--max-send-message-size- 指定最大允許 client 傳送的訊息大小
- 可以使用
bytes設定,或是42 MB這類 human readable value
-o,--output- 指定輸出的檔案位置
- 預設是
none,即透過stdout輸出
-O,--format- 設定輸出格式,如果未設定只會顯示概要
csv: 將指標以逗號分隔呈現json: 將指標以 json 格式呈現pretty: 將指標以利於閱讀的 json 格式呈現html: 將指標以 htlm 呈現influx-summary: 將指標概要以 InfluxDB line protocol 呈現influx-details: 將指標細節以 InfluxDB line protocol 呈現prometheus: 將指標概要以 Prometheus exposition 呈現.
--skipFirst- 設定忽略頭幾個 response
--connections- 設定 gRPC 連線數 (預設只有建立一條 gRPC 連線)
- 這個設定值不能大於
-c數量 - 連線數會分圴分配給 worker 使用
--connect-timeout- 設定初始化連線 timeout 時間
- 預設為
20s
--keepalive- 設定 keepalive 時間
- 只在明確設定且數值大於
0會套用
--name- 自訂 load test 名稱
--tags- 自訂的 tag
- 以
jsonstring 方式設定
--cpus- 指定用於執行 load test 的 cpu 數量
- 預設是本機的所有邏輯 cpu 總數
--debug- 指定 debug log file 的路徑
- debug log 是
json格式
-e,--enable-compression- 在 request 上啟用 gzip 壓縮
--count-errors- 啟用統計 error
- 預設
最快,最慢,平均,直方圖,延遲分佈都只計算正確回應
-v,--version- 顯示版本
-h,--help- 顯示 help 文件
- 也可以使用
--help-long與--help-man
實際使用方式
Unary call
ghz -c 20 -n 20000 --insecure --proto ./grpc6/Protos/greet.proto --call greet.Greeter.SayHello -d '{"name":"Yowko"}' 0.0.0.0:5143
client streaming
ghz -c 20 -n 20000 --insecure --proto ./grpc6/Protos/greet.proto --call greet.CandidateService.CreateCv -d '[{ "Name": "Yowko", "Jobs": [{"Title": "SRE","Salary": 10,"JobDescription": "DevOps"},{"Title": "Programmer","Salary": 5,"JobDescription": "C#"}] }, { "Name": "Test", "Jobs": [{"Title": "SRE","Salary": 8,"JobDescription": "DevOps"},{"Title": "Programmer","Salary": 7,"JobDescription": "golang"}] }]' 0.0.0.0:5143
server streaming
ghz -c 20 -n 20000 --insecure --proto ./grpc6/Protos/greet.proto --call greet.CandidateService.DownloadCv -d '{ "Name": "Yowko" }' 0.0.0.0:5143
bi-directional streaming
ghz -c 20 -n 20000 --insecure --proto ./grpc6/Protos/greet.proto --call greet.CandidateService.CreateDownloadCv -d '[{ "Name": "Yowko", "Jobs": [{"Title": "SRE","Salary": 10,"JobDescription": "DevOps"},{"Title": "Programmer","Salary": 5,"JobDescription": "C#"}] }, { "Name": "Test", "Jobs": [{"Title": "SRE","Salary": 8,"JobDescription": "DevOps"},{"Title": "Programmer","Salary": 7,"JobDescription": "golang"}] }]' 0.0.0.0:5143
心得
ghz 是我最早接觸的 gRPC load test 工具,一直以來都是用它來為 gRPC service 做基本的效能評估,以我的立場來看,優點是功能多可以滿足多數情境,缺點是功能多導致參數也多,官網的 sample 又偏向簡單,不少參數不好理解實際應用情境,另外 grpc service 的呼叫格式與其他工具(grpcurl、ghz)略有不同 ({package name}.{service name}.{procedure name} vs {package name}.{service name}/{procedure name})
參考資訊
文章作者 Yowko Tsai
上次更新 2022-04-04
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。
