文章目錄
使用 k6 來對 gRPC 做負載測試
第一次聽到 k6 是 twMVC 的活動宣傳 讓我們用 k6 來進行壓測吧,雖然後來時間因素沒有到場聽到實際應用的分享,但為了不要與技術潮流脫節還是自己找時間了解一下內容,其中第一眼的印象是使用 go 開發但以 js 做為測試腳本的語法,後來又看到 k6 支援 gRPC 的測試,當下就來興趣想做個簡單的體驗,順手紀錄一下用法
基本環境說明
- macOS Monterey 12.3
- k6 v0.37.0
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); } } }
下載與安裝
安裝 k6
k6 在多個 package manager 工具上都有上傳對應的 package
Debian/Ubuntu
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69 echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list sudo apt-get update sudo apt-get install k6Fedora/CentOS
sudo dnf install https://dl.k6.io/rpm/repo.rpm sudo dnf install k6Windows
choco install k6or
winget install k6MacOS
brew install k6
可以直接下傳 os 對應的安裝檔
使用語法與參數說明
語法
k6 [command] [flag]範例
k6command與flags說明commad
archive:建立檔案建立一個可以獨立執行的完整測試檔案
k6 archive [flags]-u,--vus int:指定 virtual user 的數量 (預設為1)-d,--duration duration:load test 持續時間-i,--iterations int:測試執行迭代次數 (以所有 virtual user 計算的總數)-s,--stage stage:增加測試階段 (語法是[duration]:[target])--execution-segment string:限定執行某個區段--execution-segment-sequence string:指定執行區段的順序-p,--paused:啟動 load test 但讓 load test 暫停--no-setup:不要執行setup()--no-teardown:不要執行teardown()--max-redirects int:限定轉導次數 (預設10)--batch int:限定平行批量 request 數量 (預設20)--batch-per-host int:限定單一 host 平行批量 request 數量 (預設6)--rps int:限定每秒 request 數量--user-agent string:指定 http request 的 user agent (預設k6/0.37.0 (https://k6.io/))--http-debug string[="headers"]:紀錄所有 HTTP requests and responses. 預設排除body可以透過--http-debug=full來加入body資訊--insecure-skip-tls-verify:不檢查 TLS 憑證的有效性--no-connection-reuse:不允許keep-alive的連線--no-vu-connection-reuse:在不同的迭代間不重複使用連線--min-iteration-duration duration:單一迭代最小的測試執行時間-w,--throw:將 warning (http request 異常) 以 error 的方式拋出--blacklist-ip ip range:將 ip range 加至 blacklist--block-hostnames pattern: 阻斷特定的 hostname 呼叫,pattern 是不限大小寫且可有可無 wildcard 開頭的 hostname--summary-trend-stats stats:指定想要顯示在概要的趨勢指標統計值--summary-time-unit string:指定概要中的趨勢統計值顯示時間單位:s,ms與us--system-tags strings:指定指標只包含的 tag (預設proto,subproto,status,method,url,name,group,check,error,error_code,tls_version,scenario,service,expected_response)--tag tag: 為所有 sample 加上 tag (格式為[name]=[value])--console-output string:將 console log 資訊以檔案格式儲存--discard-response-bodies:讀取但不處理或是儲存 HTTP response bodies--local-ips string:指定每個 virtual user 可能發出 request 的 IP Ranges and/or CIDRs (e.g. ‘192.168.220.1,192.168.0.10-192.168.0.25’, ‘fd:1::0/120’)--dns string: DNS 解析設定.- ttl:
inf保存 cache;0停用 cache (可以指定時間:1s,1m,如果沒有指定單位,會用 Milliseconds) - select:
first,randomorroundRobin. - policy:
preferIPv4,preferIPv6,onlyIPv4,onlyIPv6orany(預設ttl=5m,select=random,policy=preferIPv4)
- ttl:
--include-system-env-vars:是否將系統的 environment variables 傳給 k6 使用(預設true)--compatibility-mode string:JavaScript compiler 相容模式extendedorbase(預設extended)base: pure goja - 支援 ES5.1+ 的 Golang JS VMextended: base + 部份 ES2015 預設的 Babel (如果用到 base 不支援的語法, compile 速度對變慢)
-e,--env VAR=value:指定 environment variable--no-thresholds:不要執行 thresholds--no-summary:測試結束後不顯示概要--summary-export string:將測試結果概要儲存至指定的 JSON 路徑-O, --archive-out string:輸出檔案名稱(預設archive.tar)-h,--help:顯示 help 資訊
cloud: 在雲端執行測試將測試在 k6 的雲端服務上執行,使用
"k6 login cloud來登入k6 cloud [flags]-u,--vus int:指定 virtual user 的數量 (預設為1)-d,--duration duration:load test 持續時間-i,--iterations int:測試執行迭代次數 (以所有 virtual user 計算的總數)-s,--stage stage:增加測試階段 (語法是[duration]:[target])--execution-segment string:限定執行某個區段--execution-segment-sequence string:指定執行區段的順序-p,--paused:啟動 load test 但讓 load test 暫停--no-setup:不要執行setup()--no-teardown:不要執行teardown()--max-redirects int:限定轉導次數 (預設10)--batch int:限定平行批量 request 數量 (預設20)--batch-per-host int:限定單一 host 平行批量 request 數量 (預設6)--rps int:限定每秒 request 數量--user-agent string:指定 http request 的 user agent (預設k6/0.37.0 (https://k6.io/))--http-debug string[="headers"]:紀錄所有 HTTP requests and responses. 預設排除body可以透過--http-debug=full來加入body資訊--insecure-skip-tls-verify:不檢查 TLS 憑證的有效性--no-connection-reuse:不允許keep-alive的連線--no-vu-connection-reuse:在不同的迭代間不重複使用連線--min-iteration-duration duration:單一迭代最小的測試執行時間-w,--throw:將 warning (http request 異常) 以 error 的方式拋出--blacklist-ip ip range:將 ip range 加至 blacklist--block-hostnames pattern: 阻斷特定的 hostname 呼叫,pattern 是不限大小寫且可有可無 wildcard 開頭的 hostname--summary-trend-stats stats:指定想要顯示在概要的趨勢指標統計值--summary-time-unit string:指定概要中的趨勢統計值顯示時間單位:s,ms與us--system-tags strings:指定指標只包含的 tag (預設proto,subproto,status,method,url,name,group,check,error,error_code,tls_version,scenario,service,expected_response)--tag tag: 為所有 sample 加上 tag (格式為[name]=[value])--console-output string:將 console log 資訊以檔案格式儲存--discard-response-bodies:讀取但不處理或是儲存 HTTP response bodies--local-ips string:指定每個 virtual user 可能發出 request 的 IP Ranges and/or CIDRs (e.g. ‘192.168.220.1,192.168.0.10-192.168.0.25’, ‘fd:1::0/120’)--dns string: DNS 解析設定.- ttl:
inf保存 cache;0停用 cache (可以指定時間:1s,1m,如果沒有指定單位,會用 Milliseconds) - select:
first,randomorroundRobin. - policy:
preferIPv4,preferIPv6,onlyIPv4,onlyIPv6orany(預設ttl=5m,select=random,policy=preferIPv4)
- ttl:
--include-system-env-vars:是否將系統的 environment variables 傳給 k6 使用(預設true)--compatibility-mode string:JavaScript compiler 相容模式extendedorbase(預設extended)base: pure goja - 支援 ES5.1+ 的 Golang JS VMextended: base + 部份 ES2015 預設的 Babel (如果用到 base 不支援的語法, compile 速度對變慢)
-e,--env VAR=value:指定 environment variable--no-thresholds:不要執行 thresholds--no-summary:測試結束後不顯示概要--summary-export string:將測試結果概要儲存至指定的 JSON 路徑--exit-on-running:當測試達到運行狀態時退出--show-logs:在雲端測試時啟用顯示 log (預設true)-h,--help:顯示 help 資訊
convert:將 HAR 檔案轉成 k6 script將 HAR (HTTP Archive) 檔案轉成 k6 script
k6 convert [flags]-O,--output string:k6 script 輸出檔名 (預設stdout)--options string:將想要放進產出 k6 script 的參數以 JSON 檔案路徑提供--only strings:僅包括來自預先提供 domain 的 request--skip strings:忽略來自預先提供 domain 的 request--batch-threshold uint:批量 request idle time threshold (預設500)--no-batch:不要產生批量呼叫--enable-status-code-checks:為每個 HTTP response 加上狀態碼檢查--return-on-failed-check:如果出現非預期的回應狀態碼在迭代中回傳--correlate: 偵測在後續請求中回應的值並嘗試相應地調整腳本(目前僅限 redirects and JSON values)--min-sleep uint:每次迭代後休眠的最小秒數 (預設20)--max-sleep uint:每次迭代後休眠的最大秒數 (預設40)-h,--help:顯示 help 資訊
help:顯示 Help提供程式中所有指令的 help
k6 help [command] [flags]-h,--help:顯示 help 資訊
inspect:檢查腳本或檔案k6 inspect [file] [flags]--system-tags strings:指定指標只包含的 tag (預設proto,subproto,status,method,url,name,group,check,error,error_code,tls_version,scenario,service,expected_response)--compatibility-mode string:JavaScript compiler 相容模式extendedorbase(預設extended)base: pure goja - 支援 ES5.1+ 的 Golang JS VMextended: base + 部份 ES2015 預設的 Babel (如果用到 base 不支援的語法, compile 速度對變慢)
-e,--env VAR=value:指定 environment variable--no-thresholds:不要執行 thresholds--no-summary:測試結束後不顯示概要--summary-export string:將測試結果概要儲存至指定的 JSON 路徑-t,--type type:覆寫檔案格式,jsorarchive--execution-requirements:包括測試執行要求的計算-h,--help:顯示 help 資訊
login:服務的登入資訊當執行測試時使用
-o [type=uri]但沒有傳參數時還是會修改到預設值,使用 login 可以覆寫原本儲存的資訊k6 login [flags] k6 login [command]- Commands
cloud:Load Impact 的認證資訊influxdb:InfluxDB 的認證資訊
- Flags
-h,--help:顯示 help 資訊
- Commands
pause:暫停測試使用全域的
--addressflag 來指定 api server urlk6 pause [flags]-h,--help:顯示 help 資訊
resume:恢復測試使用全域的
--addressflag 來指定 api server urlk6 resume [flags]-h,--help:顯示 help 資訊
run: 執行 load test執行測試並且會啟動一個 REST API 來與測試互動,許多的 K6 子命令提供命令列介面來進行互動
k6 run [flags]-u,--vus int:指定 virtual user 的數量 (預設為1)-d,--duration duration:load test 持續時間-i,--iterations int:測試執行迭代次數 (以所有 virtual user 計算的總數)-s,--stage stage:增加測試階段 (語法是[duration]:[target])--execution-segment string:限定執行某個區段--execution-segment-sequence string:指定執行區段的順序-p,--paused:啟動 load test 但讓 load test 暫停--no-setup:不要執行setup()--no-teardown:不要執行teardown()--max-redirects int:限定轉導次數 (預設10)--batch int:限定平行批量 request 數量 (預設20)--batch-per-host int:限定單一 host 平行批量 request 數量 (預設6)--rps int:限定每秒 request 數量--user-agent string:指定 http request 的 user agent (預設k6/0.37.0 (https://k6.io/))--http-debug string[="headers"]:紀錄所有 HTTP requests and responses. 預設排除body可以透過--http-debug=full來加入body資訊--insecure-skip-tls-verify:不檢查 TLS 憑證的有效性--no-connection-reuse:不允許keep-alive的連線--no-vu-connection-reuse:在不同的迭代間不重複使用連線--min-iteration-duration duration:單一迭代最小的測試執行時間-w,--throw:將 warning (http request 異常) 以 error 的方式拋出--blacklist-ip ip range:將 ip range 加至 blacklist--block-hostnames pattern: 阻斷特定的 hostname 呼叫,pattern 是不限大小寫且可有可無 wildcard 開頭的 hostname--summary-trend-stats stats:指定想要顯示在概要的趨勢指標統計值--summary-time-unit string:指定概要中的趨勢統計值顯示時間單位:s,ms與us--system-tags strings:指定指標只包含的 tag (預設proto,subproto,status,method,url,name,group,check,error,error_code,tls_version,scenario,service,expected_response)--tag tag: 為所有 sample 加上 tag (格式為[name]=[value])--console-output string:將 console log 資訊以檔案格式儲存--discard-response-bodies:讀取但不處理或是儲存 HTTP response bodies--local-ips string:指定每個 virtual user 可能發出 request 的 IP Ranges and/or CIDRs (e.g. ‘192.168.220.1,192.168.0.10-192.168.0.25’, ‘fd:1::0/120’)--dns string: DNS 解析設定.- ttl:
inf保存 cache;0停用 cache (可以指定時間:1s,1m,如果沒有指定單位,會用 Milliseconds) - select:
first,randomorroundRobin. - policy:
preferIPv4,preferIPv6,onlyIPv4,onlyIPv6orany(預設ttl=5m,select=random,policy=preferIPv4)
- ttl:
--include-system-env-vars:是否將系統的 environment variables 傳給 k6 使用(預設true)--compatibility-mode string:JavaScript compiler 相容模式extendedorbase(預設extended)base: pure goja - 支援 ES5.1+ 的 Golang JS VMextended: base + 部份 ES2015 預設的 Babel (如果用到 base 不支援的語法, compile 速度對變慢)
-e,--env VAR=value:指定 environment variable--no-thresholds:不要執行 thresholds--no-summary:測試結束後不顯示概要--summary-export string:將測試結果概要儲存至指定的 JSON 路徑-o,--out uri:外部指標儲存服務的 uri--no-usage-report:不要使用匿名統計資訊-t,--type type:覆寫檔案格式,jsorarchive-h,--help:顯示 help 資訊
scale:擴展測試使用全域的
--addressflag 來指定 api server urlk6 scale [flags]-h,--help:顯示 help 資訊-u,--vus int:指定 virtual user 的數量 (預設為1)-m,--max int:最大可用的 virtual user 的數量
stats:顯示測試指標使用全域的
--addressflag 來指定 api server urlk6 stats [flags]-h,--help:顯示 help 資訊
status:顯示測試狀態使用全域的
--addressflag 來指定 api server urlk6 status [flags]-h,--help:顯示 help 資訊
version:顯示程式版本顯示程式版本並退出
k6 version [flags]-h,--help:顯示 help 資訊
Global Flags:
-a,--address string:api server 的位址 (預設localhost:6565)-c,--config string:JSON config file (預設/Users/{username}/Library/Application Support/loadimpact/k6/config.json)--log-output string:修改 k6 log 的輸出,允許使用:stderr,stdout,none,loki[=host:port],file[=./path.fileformat](預設stderr)--logformat stringlog output format--no-color:停用輸出的顏色顯示-q,--quiet:停用進度更新-v,--verbose:啟用詳細日誌記錄
使用方式
Unary call
測試腳本
// import grpc package import grpc from 'k6/net/grpc'; import { check, sleep } from 'k6'; // 建立 grpc client const client = new grpc.Client() //載入 proto 檔:第一個參數是 proto 檔的路徑,第二個參數是 proto 檔名 client.load(['Protos'], 'greet.proto'); export default () => { //連線至 grpc server endpoint client.connect('localhost:5143', { // 允許使用未加密的連線,預設是 `false` plaintext: true }); const data = { name: 'Yowko' }; // 呼叫 grpc 服務:{package name}.{service name}/{procedure name} const response = client.invoke('greet.Greeter/SayHello', data); // 檢查回應是否為 `grpc.StatusOK` check(response, { 'status is OK': (r) => r && r.status === grpc.StatusOK, }); // log 回應 //console.log(JSON.stringify(response.message)); // 關閉連線 client.close(); // sleep 一秒 //sleep(1); };測試指令
k6 run -i 20000 -u 20 k6.js
client streaming
暫不支援
server streaming
暫不支援
bi-directional streaming
暫不支援
心得
使用 js 做為腳本語法
我個人 js 底子差,寫起來特別不順手XD
不支援 streaming 測試
GitHub Issue: Support gRPC streaming 暫時還不支援 streaming 在使用上有不少限制
測試結果一目瞭然
這個是個人喜好,我覺得有上色,看起來賞心悅目許多XD
測試效率較差?
與 ghz 相比,一樣使用 20 concurrent 與 20000 total request 測試相同的 grpc service,ghz 可以壓出 9000 Requests/sec,但 k6 只有 4886.656447/s,不過也可能是寫法沒有優化的關係,只是兩者我都是參考官網建議寫的
參數超多
我在官網上沒有找到完整文件,cli 的 help 文件也有點亂:global flag、flag、subcommand….東西真的很多
參考資訊
文章作者 Yowko Tsai
上次更新 2022-04-08
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。
