使用 JSON 協議的 gRPC

polarisxu發表於2020-08-13

JSON payload 實現簡易的請求和響應的內省。

介紹

大家經常說 gRPC 是基於 Google Protocol Buffers payload 格式的,然而這不完全正確。gRPC payload 的預設格式是 Protobuf,但是 gRPC-Go 的實現中也對外暴露了 Codec interface ,它支援任意的 payload 編碼。我們可以使用任何一種格式,包括你自己定義的二進位制格式、flatbuffers、或者使用我們今天要討論的 JSON ,作為請求和響應。

服務端準備

我已經基於 JSON payload 實現grpc/encoding.Codec,建立了一個示例庫。服務端的準備工作僅僅像引入一個包那樣簡單;

import _ "github.com/johanbrandhorst/grpc-json-example/codec"

這行程式碼註冊了一個基於 json 內容的子型別 JSON Codec,我們在後面會看到這對於方便記憶很重要。

Request 示例

gRPC 客戶端

使用 gRPC 客戶端,你只需要使用合適的內容子型別作為 grpc.DialOption 來初始化:

import "github.com/johanbrandhorst/grpc-json-example/codec"
func main() {
    conn := grpc.Dial("localhost:1000",
        grpc.WithDefaultCallOptions(grpc.CallContentSubtype(codec.JSON{}.Name())),
    )
}

示例庫程式碼包含有完整示例的客戶端

cURL

更有趣的是,現在我們可以用 cURL 寫出請求(和讀取響應)!請求示例:

$ Echo -en '\x00\x00\x00\x00\x17{"id":1,"role":"ADMIN"}' | curl -ss -k --http2 \
        -H "Content-Type: application/grpc+json" \
        -H "TE:trailers" \
        --data-binary @- \
        https://localhost:10000/example.UserService/AddUser | od -bc
0000000 000 000 000 000 002 173 175
         \0  \0  \0  \0 002   {   }
0000007
$ Echo -en '\x00\x00\x00\x00\x17{"id":2,"role":"GUEST"}' | curl -ss -k --http2 \
        -H "Content-Type: application/grpc+json" \
        -H "TE:trailers" \
        --data-binary @- \
        https://localhost:10000/example.UserService/AddUser | od -bc
0000000 000 000 000 000 002 173 175
         \0  \0  \0  \0 002   {   }
0000007
$ Echo -en '\x00\x00\x00\x00\x02{}' | curl -k --http2 \
        -H "Content-Type: application/grpc+json" \
        -H "TE:trailers" \
        --data-binary @- \
        --output - \
        https://localhost:10000/example.UserService/ListUsers
F{"id":1,"role":"ADMIN","create_date":"2018-07-21T20:18:21.961080119Z"}F{"id":2,"role":"GUEST","create_date":"2018-07-21T20:18:29.225624852Z"}

解釋

使用 cURL 傳送請求需要手動把 gRPC HTTP2 message payload header 加到 payload:

'\x00\x00\x00\x00\x17{"id":1,"role":"ADMIN"}'
#<-->----------------------------------------- Compression boolean (1 byte)
#    <-------------->------------------------- Payload size (4 bytes)
#                    <--------------------->-- JSON payload

請求頭必須包含 TE 和正確的 Content-Type

-H "Content-Type: application/grpc+json" -H "TE:trailers"

Content-Type 頭中 application/grpc+ 後的字串需要與服務端註冊的 codec 的 Name() 相吻合。這就是內容子類.

endpoint 需要與 proto 包的名字、服務和方法三者的名字都匹配:

https://localhost:10000/example.UserService/AddUser

響應頭與請求頭一致:

'\0  \0  \0  \0 002   {   }'
#<-->------------------------ Compression boolean (1 byte)
#    <------------>---------- Payload size (4 bytes)
#                     <--->-- JSON payload

總結

我們已經展示了我們可以輕易地在 gRPC 中使用 JSON payload,甚至可以用 JSON payload 直接傳送 cURL 請求到我們的 gRPC 服務,沒有代理,沒有 grpc 閘道器,除了引入一個必要的包也沒有其他的準備工作。

如果你對本文感興趣,或者有任何問題和想法,請在 @johanbrandhorst 上或 在 Gophers Slack jbrandhorst 下聯絡我。很高興聽到你的想法。


via: https://jbrandhorst.com/post/grpc-json/

作者:Johan Brandhorst 譯者:lxbwolf 校對:polaris1119

本文由 GCTT 原創編譯,Go語言中文網 榮譽推出

相關文章