GRPC

简介

  • A high performance, open source universal RPC framework.
  • gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
  • 官方文档[https://grpc.io/](https://grpc.io/),go github[https://github.com/grpc/grpc-go](https://github.com/grpc/grpc-go)
  • GRPC即Google RPC(Remote Procedure Call),支持多种语言实现,通信时默认采用Protocol Buffers作为序列化协议
  • go依赖的grpc模块以前是维护在golang.org上的,所以依赖是google.golang.org/grpc,但是后来grpc-go代码维护转到了github上,即[https://github.com/grpc/grpc-go](https://github.com/grpc/grpc-go),但是实际使用中依赖仍然是google.golang.org/grpc,注意实际代码是维护在github上的;如果go get google.golang.org/grpc下载不了,可以尝试从github clone项目并改名为google.golang.org/grpc

git clone [https://github.com/grpc/grpc-go.git](https://github.com/grpc/grpc-go.git) $GOPATH/src/google.golang.org/grpc

Protocol Buffers

简介

  • Protocol buffers provide a language-neutral, platform-neutral, extensible mechanism for serializing structured data in a forward-compatible and backward-compatible way. It’s like JSON, except it’s smaller and faster, and it generates native language bindings.
  • Protocol buffers are a combination of the definition language (created in .proto files), the code that the proto compiler generates to interface with data, language-specific runtime libraries, and the serialization format for data that is written to a file (or sent across a network connection).
  • 官方文档[https://developers.google.com/protocol-buffers/docs/overview](https://developers.google.com/protocol-buffers/docs/overview),go github

[https://github.com/golang/protobuf](https://github.com/golang/protobuf)

  • Protobuf目前有v2和v3两个版本
  • Protobuf具有很好的兼容性
    • A和B模块使用同一份protobuf通信协议
    • 向后兼容(backend compatible)即当前版本设计能够兼容以前的版本,B升级新协议后,A发送旧版本数据,B仍然能够正常接收
    • 向前兼容(forward compatible)即当前版本设计能够兼容未来版本,B未升级,A升级新版本协议,A发送的新版本数据,B仍然能够正常接收
  • v1.4版本以后的google.golang.org/protobuf维护在github.com/golang/protobuf

Proto2

Message

  • 定义消息格式为[field desc] [type] [field name] [number]

    • reuqired 为必传参数,如果客户端请求/服务端响应没有该字段,都会导致请求出错
    • optional 可选参数
    • repeated 数组类型,同样是可选的
    • 可以在字段后面添加默认值描述
      1. message HelloRequest {
      2. required string name = 1;
      3. optional int32 age = 2 [default = 10];
      4. repeated string pets = 3;
      5. }
  • 保留字段

    • 序号19000-19999为FieldDescriptor的保留字段
    • 业务迭代过程中,序号不应该重用,如果某些字段需要删掉/保留某些序号,可以使用reserved关键字,如果使用了保留字段,编译时会报错
      1. message Foo {
      2. reserved 2, 15, 9 to 11; //序号保留
      3. reserved "foo", "bar"; //字段名保留
      4. optional string name = 3;
      5. required int32 age = 4;
      6. repeated string pets = 5;
      7. }
  • 枚举类型,如果想对同一个枚举值设置多个name,可以启动allow_alias参数,否则多个name对应同一个枚举值会编译报错

    1. message HelloRequest {
    2. optional Week today = 4 [default = Mon];
    3. }
    4. enum Week {
    5. //option allow_alias = true;
    6. //NewMon = 0;
    7. Mon = 0;
    8. Tue = 1;
    9. Wed = 2;
    10. Thu = 3;
    11. Fri = 5;
    12. }
  • 如果一个.proto文件需要依赖其他文件中定义的Message,可以使用import来导入,注意编译的时候需要在--proto_path/-I指定import文件所在路径。此外还有import public用法,详情参考官方;proto2的协议可以import proto3的依赖

  • 嵌套类型Nested Type:注意只会A嵌套了B,编译出来A并不会包含B,只是B的结构体名是嵌套的。另外proto定义时嵌套类型外部也是可以使用的

    1. message HelloRequest {
    2. message Content {
    3. optional string msg = 1;
    4. }
    5. }
    6. message HelloReply {
    7. optional HelloRequest.Content content = 1;
    8. }
    9. //编译结果
    10. type HelloRequest struct {
    11. }
    12. type HelloRequest_Content struct {
    13. Msg *string ...
    14. }
  • One of和Map类型

    1. message CommReq {
    2. map<string, ValueMsg> my_map = 1;
    3. oneof my_oneOf {
    4. string name = 2;
    5. ValueMsg v = 3;
    6. int32 age = 4;
    7. }
    8. }

    Extensions

  • 当我们的基础协议被外部项目使用时,有可能外部项目需要新增一些扩展字段,这时候可以在定义基础协议时,预留Extensions字段,外部项目需要时,可以进行扩展 ```protobuf //comm.proto message CommReq { extensions 1 to 10; //预留1-10序号 optional int32 code = 11; }

//hello.proto import “comm.proto”; extend comm.CommReq { optional string msg = 1; } message HelloRequest { optional comm.CommReq req = 1; }

//go中传递和获取extensions字段 err = proto.SetExtension(req.GetReq(), pb.EMsg, proto.String(“hello”)) str, := proto.GetExtension(req.GetReq(), pb.E_Msg)

  1. <a name="dBD5w"></a>
  2. ### Options
  3. - 可以在定义proto时,使用一些option,他们不改变proto文件的数据含义,但是可以使用他们来针对不同情况对数据进行不同处理
  4. - 官方的Option定义在`**google/protobuf/descriptor.proto**`,Option可以有file-level,message-level,field-level,service-level,method-level
  5. - 常见的`option go_package`就是file-level的option
  6. - 自定义Option,`**google/protobuf/descriptor.proto**`中预留了一些扩展字段,用户可以对各个级别的Option进行拓展
  7. ```protobuf
  8. //comm.proto
  9. import "google/protobuf/descriptor.proto";
  10. extend google.protobuf.MessageOptions {
  11. optional string my_option = 50012;
  12. }
  13. message CommReq {
  14. option (my_option) = "just_demo";
  15. optional int32 code = 1;
  16. optional string msg = 2;
  17. }
  18. //hello.proto
  19. import "comm.proto";
  20. message HelloRequest {
  21. option (comm.my_option) = "no option"; //也可以引用其他proto定义的option,注意括号
  22. optional comm.CommReq req = 1;
  23. }
  24. //option在proto已经定义值了,程序中只需要读取值,由于是定义在descriptor中的,所以需要拿到
  25. //该数据结构定义的descriptor
  26. fileDesc, msgDesc := descriptor.MessageDescriptorProto(new(pb.CommReq))
  27. op, err := proto.GetExtension(msgDesc.GetOptions(), pb.E_MyOption)

Proto3