玩转GRPC(1)-整合JSON编码方式
版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | https://vearne.cc
引言
提到GRPC大家想到的就是 HTTP2 + protobuf。HTTP2能够实现多路复用, protobuf提高数据传输的压缩率。但其实GRPC还可以跟Thrift、JSON等编码方式进行整合。
gRPC lets you use encoders other than Protobuf. It has no dependency on Protobuf and was specially made to work with a wide variety of environments. We can see that with a little extra boilerplate, we can use any encoder we want. While this post only covered JSON, gRPC is compatible with Thrift, Avro, Flatbuffers, Cap’n Proto, and even raw bytes! gRPC lets you be in control of how your data is handled. (We still recommend Protobuf though due to strong backwards compatibility, type checking, and performance it gives you.)
你只需要实现
// Codec defines the interface gRPC uses to encode and decode messages. Note
// that implementations of this interface must be thread safe; a Codec's
// methods can be called from concurrent goroutines.
type Codec interface {
// Marshal returns the wire format of v.
Marshal(v interface{}) ([]byte, error)
// Unmarshal parses the wire format into v.
Unmarshal(data []byte, v interface{}) error
// Name returns the name of the Codec implementation. The returned string
// will be used as part of content type in transmission. The result must be
// static; the result cannot change between calls.
Name() string
}
例子
test_grpc-server.go
package main
import (
"encoding/json"
"fmt"
"google.golang.org/grpc/encoding"
"log"
"net"
"golang.org/x/net/context"
"google.golang.org/grpc"
_ "google.golang.org/grpc/encoding/gzip" // 会完成gzip Compressor的注册
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"google.golang.org/grpc/reflection"
)
const (
port = ":50051"
)
type server struct{}
type JsonCodec struct {
}
func (codec *JsonCodec) Name() string {
return "json"
}
func (codec *JsonCodec) Marshal(v interface{}) ([]byte, error) {
return json.Marshal(v)
}
func (codec *JsonCodec) Unmarshal(data []byte, v interface{}) error {
return json.Unmarshal(data, v)
}
func init() {
// server段和client段都需要注册
encoding.RegisterCodec(&JsonCodec{})
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
fmt.Println("pb.HelloRequest", in.Name)
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
// Register reflection service on gRPC server.
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
test_grpc-client.go
package main
import (
"encoding/json"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
_ "google.golang.org/grpc/encoding/gzip" // 会完成gzip Compressor的注册
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"log"
"os"
"time"
)
const (
address = "localhost:50051"
defaultName = "world"
)
type JsonCodec struct {
}
func (codec *JsonCodec) Name() string {
return "json"
}
func (codec *JsonCodec) Marshal(v interface{}) ([]byte, error) {
return json.Marshal(v)
}
func (codec *JsonCodec) Unmarshal(data []byte, v interface{}) error {
return json.Unmarshal(data, v)
}
func main() {
// server段和client段都需要注册
encoding.RegisterCodec(&JsonCodec{})
conn, err := grpc.Dial(address,
grpc.WithDefaultCallOptions(grpc.CallContentSubtype("json"), grpc.UseCompressor("gzip")),
grpc.WithInsecure())
//grpc.DialContext()
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
这里不仅指定了数据的编码方式,还指定了对编码后的数据可以进行gzip压缩。
使用wireshark抓包
在request message中,能够看到HEADER frame中, header信息标识了请求的path
content-type
同样在response message中,也能够看到 content-type
的变化