添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

RESTful API 時代,我們有許多簡單好用的測試工具:有酷炫的 Postman ,有命令列控愛用的 HTTPie ,當然也有硬漢必備的萬用瑞士刀 curl

那麼,gRPC 呢?

這篇文章介紹兩個好用的小工具: gRPCurl ghz ,一個是輸入輸出介面測試工具,另一個是壓測工具,也順便介紹一些簡化測試的技巧。

待測程式放在 https://github.com/William-Yeh/grpcurl-and-ghz-demo

檔案簡介: │   ├── server │   └── server-new ├── routeguide ← gRPC 介面定義;原封不動取自 "gRPC-Go" 專案 │   ├── route_guide.pb.go │   └── route_guide.proto ├── server ← server 程式;原封不動取自 "gRPC-Go" 專案 │   └── server.go ├── server-new ← 由我修改過的新版 server 程式 │   └── server-new.go ├── testdata.dat ← 給 gRPCurl 的測試資料 └── testdata.json ← 給 ghz 的測試資料

為了方便起見,我挑選 “ gRPC-Go ” 專案裡面的 “ the route guide server and client ” 範例做為待測程式。

這隻 server 程式透過 gRPC 提供 RouteGuide 服務,我們這次只會測試其中的 RecordRoute 呼叫。以下是從 gRPC 介面定義檔 route_guide.proto 摘錄我們會用到的部份:

package routeguide;
service RouteGuide {
  //...
  // A client-to-server streaming RPC.
  // Accepts a stream of Points on a route being traversed, returning a
  // RouteSummary when traversal is completed.
  rpc RecordRoute(stream Point) returns (RouteSummary) {}
message Point {
  int32 latitude = 1;
  int32 longitude = 2;
message RouteSummary {
  int32 point_count = 1;
  int32 feature_count = 2;
  int32 distance = 3;
  int32 elapsed_time = 4;
//...

我也針對 server 程式小改三個地方,弄出另一個 server-new 程式:

$ diff  server/server.go  server-new/server-new.go
42a43
> 	"google.golang.org/grpc/reflection"
55c56
< 	port       = flag.Int("port", 10000, "The server port")
> 	port       = flag.Int("port", 20000, "The server port")
243a245,248
> 	// Register reflection service on gRPC server.
> 	reflection.Register(grpcServer)

簡單來說,新舊兩版的差別是:

  • 舊版 server 的 gRPC port = 10000,新版 server-new 則是 20000。
  • 新版 server-new 支援 server reflection 功能。稍後會再說明這是什麼。
  • 實驗所需環境:

  • Go 1.14 以上。
  • gRPCurl 1.5.0 以上。
  • ghz 0.51.0 以上。
  • 先來編譯程式:

    $ ./build.sh
    

    成功後,會在 out 目錄拿到兩份執行檔:

    out/server :原封不動來自 “gRPC-Go” 專案的 server 程式。

    out/server-new :由我小小修改過的新程式。

    進行實驗時,建議將你的終端機配置成這樣:

    gRPCurl

    gRPCurl ,顧名思義,是在向硬漢必備的萬用瑞士刀 curl 致敬。不妨將它視為 gRPC 版的 curl。

    gRPCurl 使用上最主要的差別是,因應 gRPC 的特性,必須餵給它 proto 檔案,才會知道該如何封裝訊息格式。

    譬如說,我們可將 proto 檔案的路徑寫在 -import-path 中、將 proto 檔案名稱寫在 -proto 中、將參數寫在 -d 中,再呼叫 server 的遠端程序:

    $ grpcurl -plaintext  \
        -d '{"latitude":-460000000,"longitude":-1160000000} {"latitude":720000000,"longitude":-540000000}' \
        -import-path ./routeguide        \
        -proto       route_guide.proto   \
        127.0.0.1:10000                  \
        routeguide.RouteGuide.RecordRoute
      "pointCount": 2,
      "distance": 13975745
    

    我們也可將事先備妥的資料檔餵給 gRPCurl。像是含有 100 筆資料的 testdata.dat

    $ grpcurl -plaintext -d '@'            \
        -import-path ./routeguide          \
        -proto       route_guide.proto     \
        127.0.0.1:10000                    \
        routeguide.RouteGuide.RecordRoute  \
      < testdata.dat
      "pointCount": 100,
      "distance": 1003784333
    

    ghz 壓測工具,是這麼自我介紹的:

    Simple gRPC benchmarking and load testing tool inspired by hey and grpcurl.

    所以,從命令列參數及統計結果上,都可看出它們的影響。

    譬如說,我們可將 proto 檔案的路徑寫在 --import-path 中、將 proto 檔案名稱寫在 --proto 中、將參數寫在 --data 中,再呼叫 server 的遠端程序:

    $ ghz --insecure  -z 20s  \
          --data '[{"latitude":-460000000,"longitude":-1160000000},{"latitude":720000000,"longitude":-540000000}]' \
          --import-paths ./routeguide         \
          --proto        route_guide.proto    \
          --call routeguide.RouteGuide.RecordRoute  \
          127.0.0.1:10000
        壓測 20 秒,結果如下:
    Response time histogram:
      0.123 [1]     |
      5.147 [543463]        |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
      10.170 [4297] |
      15.194 [271]  |
      20.217 [8]    |
      25.241 [3]    |
      30.264 [1]    |
      35.288 [0]    |
      40.311 [0]    |
      45.335 [0]    |
      50.358 [1]    |
    Latency distribution:
      10 % in 0.90 ms
      25 % in 1.21 ms
      50 % in 1.58 ms
      75 % in 2.04 ms
      90 % in 2.66 ms
      95 % in 3.24 ms
      99 % in 4.93 ms
    Status code distribution:
      [Canceled]      33 responses
      [Unavailable]   4 responses
      [OK]            548045 responses
    Error distribution:
      [33]   rpc error: code = Canceled desc = grpc: the client connection is closing
      [4]    rpc error: code = Unavailable desc = transport is closing

    同樣的,我們也可將事先備妥的資料檔餵給 ghz。像是含有 100 筆資料的 testdata.json

    $ ghz --insecure --data=@  -z 20s  \
          --import-paths ./routeguide         \
          --proto        route_guide.proto    \
          --call routeguide.RouteGuide.RecordRoute  \
          127.0.0.1:10000  \
      < testdata.json
        壓測 20 秒,結果如下:
    Response time histogram:
      0.126 [1]     |
      2.915 [772021]        |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
      5.705 [15003] |∎
      8.495 [1000]  |
      11.285 [458]  |
      14.074 [95]   |
      16.864 [10]   |
      19.654 [12]   |
      22.444 [3]    |
      25.233 [5]    |
      28.023 [40]   |
    Latency distribution:
      10 % in 0.65 ms
      25 % in 0.86 ms
      50 % in 1.11 ms
      75 % in 1.42 ms
      90 % in 1.88 ms
      95 % in 2.30 ms
      99 % in 3.57 ms
    Status code distribution:
      [OK]         788648 responses
      [Canceled]   32 responses
    Error distribution:
      [32]   rpc error: code = Canceled desc = grpc: the client connection is closing

    實驗二:不需搭配 proto 檔

    使用前,每次都要先備妥待測程式的 proto 檔,其實也滿麻煩的。萬一複雜的 proto 檔案又去 import 其他 proto 檔 1,可能就得條列一堆 -import-path--import-path 命令列參數給 gRPCurl 及 ghz。

    有沒有省力一點的方法?

    有的,就是透過 server reflection 功能。

    Server reflection

    我以這次的範例程式 server-new 為例,說明如何加上 server reflection 功能。

    首先,請加上 google.golang.org/grpc/reflection 套件:

    import "google.golang.org/grpc/reflection"
    

    接著,在 grpcServer.Serve 之前,呼叫 reflection.Register

    grpcServer := grpc.NewServer(opts...)
    //...
    // Register reflection service on gRPC server.
    reflection.Register(grpcServer)
    grpcServer.Serve(lis)
    

    只需要這兩個步驟,你的 gRPC 程式本身就具有 server reflection 功能,對方不再需要 proto 檔案就能直接進行遠端呼叫。

    針對新版的 server-new 來實驗看看吧。

    gRPCurl

    我們可用 list 指令查詢 server-new 提供哪些服務:

    $ grpcurl -plaintext  127.0.0.1:20000  list
    grpc.reflection.v1alpha.ServerReflection
    routeguide.RouteGuide
    

    可用 describe 指令查詢 server-new 提供服務的介面:

    $ grpcurl -plaintext  127.0.0.1:20000  describe
    grpc.reflection.v1alpha.ServerReflection is a service:
    service ServerReflection {
      rpc ServerReflectionInfo ( stream .grpc.reflection.v1alpha.ServerReflectionRequest ) returns ( stream .grpc.reflection.v1alpha.ServerReflectionResponse );
    routeguide.RouteGuide is a service:
    service RouteGuide {
      rpc GetFeature ( .routeguide.Point ) returns ( .routeguide.Feature );
      rpc ListFeatures ( .routeguide.Rectangle ) returns ( stream .routeguide.Feature );
      rpc RecordRoute ( stream .routeguide.Point ) returns ( .routeguide.RouteSummary );
      rpc RouteChat ( stream .routeguide.RouteNote ) returns ( stream .routeguide.RouteNote );
    

    可用 describe 指令進一步查詢某參數的具體格式:

    $ grpcurl -plaintext  127.0.0.1:20000  describe .routeguide.Point
    routeguide.Point is a message:
    message Point {
      int32 latitude = 1;
      int32 longitude = 2;
    

    可看出,gRPCurl 不需要 proto 檔案,就能直接向 server-new 查詢遠端呼叫所需知道的介面細節。

    最後,讓我們將含有 100 筆資料的 testdata.dat 餵給 gRPCurl 來測測看:

    $ grpcurl -plaintext -d '@' \
        127.0.0.1:20000         \
        routeguide.RouteGuide.RecordRoute  \
      < testdata.dat
      "pointCount": 100,
      "distance": 1003784333
    

    有了 server reflection 功能,是不是方便多了?如果你有權修改原始程式,這是值得好好考慮的,可讓測試工作輕鬆一點。

    當然啦,你可以考慮在 production 環境關掉這功能;但在測試環境中,這真的很方便。

    儘管 server reflection 通常不會在 production 環境啟用,不過我還是很好奇:server reflection 對執行效率的影響有多少?尤其是涉及 marshalling。

    用 ghz 試試看吧!

    $ ghz --insecure --data=@  -z 20s  \
          --call routeguide.RouteGuide.RecordRoute  \
          127.0.0.1:20000  \
      < testdata.json
        壓測 20 秒,結果如下:
    Response time histogram:
      0.118 [1]     |
      2.085 [776730]        |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
      4.052 [45892] |∎∎
      6.019 [3018]  |
      7.986 [550]   |
      9.953 [112]   |
      11.920 [74]   |
      13.887 [51]   |
      15.854 [37]   |
      17.821 [17]   |
      19.788 [1]    |
    Latency distribution:
      10 % in 0.62 ms
      25 % in 0.82 ms
      50 % in 1.06 ms
      75 % in 1.36 ms
      90 % in 1.80 ms
      95 % in 2.20 ms
      99 % in 3.38 ms
    Status code distribution:
      [OK]            826483 responses
      [Canceled]      40 responses
      [Unavailable]   1 responses
    Error distribution:
      [40]   rpc error: code = Canceled desc = grpc: the client connection is closing
      [1]    rpc error: code = Unavailable desc = transport is closing

    雖然這還不算非常嚴謹的實驗,但可看出,沒有 server reflection 功能的 server 版本,與此功能的 server-new 版本,執行效率沒有顯著差異。

    因此,server reflection 功能,值得嘗試。

    本篇文章,介紹兩個好用的 gRPC 測試小工具:輸入輸出介面測試工具 gRPCurl,以及壓測工具 ghz。最後並推薦 server reflection 功能來簡化 gRPC 測試工作。

    ghz 也有 web 介面,目前是 beta 狀態。有興趣的,請去 ghz 官網看看。