JSON 协议 payload 的 gRPC 用法
介绍
大家经常说 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 网关,除了引入一个必要的包也没有其他的准备工作。