版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | http://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抓包
image_1doj6p41u138p1g9i75r1bs11dql9.png-263.6kB
在request message中,能够看到HEADER frame中, header信息标识了请求的path content-type
image_1doj730hf7h317091k4l1jr01mogm.png-175.5kB
同样在response message中,也能够看到 content-type的变化

参考资料

  1. gRPC + JSON
  2. encoding

如果我的文章对你有帮助,你可以给我打赏以促使我拿出更多的时间和精力来分享我的经验和思考总结。

微信支付码

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据