編寫一個介面壓測工具

crossoverJie發表於2021-11-15

前言

前段時間有個專案即將上線,需要對其中的核心介面進行壓測;由於我們的介面是 gRPC 協議,找了一圈發現壓測工具並不像 HTTP 那麼多。

最終發現了 ghz 這個工具,功能也非常齊全。

事後我在想為啥做 gRPC 壓測的工具這麼少,是有什麼難點嘛?為了驗證這個問題於是我準備自己寫一個工具。

<!--more-->

特性

前前後後大概花了個週末的時間完成了相關功能。

https://github.com/crossoverJie/ptg/

也是一個命令列工具,使用起來效果如上圖;完整的命令如下:

NAME:
   ptg - Performance testing tool (Go)

USAGE:
   ptg [global options] command [command options] [arguments...]

COMMANDS:
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --thread value, -t value              -t 10 (default: 1 thread)
   --Request value, --proto value        -proto http/grpc (default: http)
   --protocol value, --pf value          -pf /file/order.proto
   --fully-qualified value, --fqn value  -fqn package.Service.Method
   --duration value, -d value            -d 10s (default: Duration of test in seconds, Default 10s)
   --request value, -c value             -c 100 (default: 100)
   --HTTP value, -M value                -m GET (default: GET)
   --bodyPath value, --body value        -body bodyPath.json
   --header value, -H value              HTTP header to add to request, e.g. "-H Content-Type: application/json"
   --target value, --tg value            http://gobyexample.com/grpc:127.0.0.1:5000
   --help, -h                            show help (default: false)

考慮到受眾,所以同時支援 HTTPgRPC 介面的壓測。

gRPC 壓測時所需的引數要多一些:

ptg -t 10 -c 100 -proto grpc  -pf /xx/xx.proto -fqn hello.Hi.Say -body test.json  -tg "127.0.0.1:5000"

比如需要提供 proto 檔案的路徑、具體的請求引數還有請求介面的全路徑名稱。

目前只支援最常見的 unary call 呼叫,後續如果有需要的話也可以 stream。

同時也支援壓測時間、次數兩種壓測方式。

安裝

想體驗度朋友如果本地有 go 環境那直接執行:

go get github.com/crossoverJie/ptg

沒有環境也沒關係,可以再 release 頁面下載與自己環境對應的版本解壓使用。


https://github.com/crossoverJie/ptg/releases

設計模式

整個開發過程中還是有幾個點想和大家分享,首先是設計模式。

因為一開始設計時就考慮到需要支援不同的壓測模式(次數、時間;後續也可以新增其他的模式)。

所以我便根據壓測的生命週期定義了一套介面:

type (
    Model interface {
        Init()
        Run()
        Finish()
        PrintSate()
        Shutdown()
    }
)    

從名字也能看出來,分別對應:

  • 壓測初始化
  • 執行壓測
  • 停止壓測
  • 列印壓測資訊
  • 關閉程式、釋放資源


然後在兩個不同的模式中進行實現。

這其實就是一個典型的依賴倒置原則。

程式設計師要依賴於抽象介面程式設計、不要依賴具體的實現。

其實大白話就是我們們 Java 裡常說的面向介面程式設計;這個程式設計技巧在開發框架、SDK或是多種實現的業務中常用。

好處當然是顯而易見:
當介面定義好之後,不同的業務只需要根據介面實現自己的業務就好,完全不會互相影響;維護、擴充套件都很方便。

支援 HTTPgRPC 也是同理實現的:

type (
    Client interface {
        Request() (*Response, error)
    }
)    


當然前提得是前期的介面定義需要考慮周全、不能之後頻繁修改介面定義,這樣的介面就沒有意義了。

goroutine

另外一點則是不得不感嘆 goroutine+select+channel 這套併發程式設計模型真的好用,並且也非常容易理解。

很容易就能寫出一套併發程式碼:

func (c *CountModel) Init() {
    c.wait.Add(c.count)
    c.workCh = make(chan *Job, c.count)
    for i := 0; i < c.count; i++ {
        go func() {
            c.workCh <- &Job{
                thread:   thread,
                duration: duration,
                count:    c.count,
                target:   target,
            }
        }()
    }
}

比如這裡需要初始化 N 個 goroutine 執行任務,只需要使用 go 關鍵字,然後利用 channel 將任務寫入。

當然在使用 goroutine+channel 配合使用時也得小心 goroutine 洩露的問題;簡單來說就是在程式設計師退出時還有 goroutine 沒有退出。

比較常見的例子就是向一個無緩衝的 channel 中寫資料,當沒有其他 goroutine 來讀取數時,寫入的 goroutine 就會被一直阻塞,最終導致洩露。

總結

gRPC 介面壓測需求的朋友歡迎試用,提出寶貴意見;當然 HTTP 介面也可以。

原始碼地址:
https://github.com/crossoverJie/ptg/

相關文章