GRPC-语法定义

GRPC-语法定义

从官方示例入手

gitlub地址

​ git地址:https://github.com/grpc/grpc-java.git

​ 分析工程中 examples/protos/route_guide.proto文件

// 
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.routeguide";
option java_outer_classname = "RouteGuideProto";
option objc_class_prefix = "RTG";

package routeguide;

service RouteGuide {
 
  rpc GetFeature(Point) returns (Feature) {}

  rpc ListFeatures(Rectangle) returns (stream Feature) {}

  rpc RecordRoute(stream Point) returns (RouteSummary) {}

  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

message Rectangle {
  Point lo = 1;
  Point hi = 2;
}

message Feature {
  string name = 1;
  Point location = 2;
}

message FeatureDatabase {
  repeated Feature feature = 1;
}

message RouteNote {

  Point location = 1;

  string message = 2;
}

message RouteSummary {
  
  int32 point_count = 1;

  int32 feature_count = 2;

  int32 distance = 3;

  int32 elapsed_time = 4;
}

上述是一个完整的示例代码,接下来咱们逐个分析。

eg:这里使用PB代指 protocol buffers

文件定义

​ gRPC的文件都是以.proto结尾

消息类型定义(Message)

一个message类型看上去很像一个Java class,由多个字段组成。每一个字段都由类型、名称组成,位于等号右边的值不是字段默认值,而是数字标签,可以理解为字段身份的标识符,类似于数据库中的主键,不可重复,标识符用于在编译后的二进制消息格式中对字段进行识别,一旦你的pb消息投入使用,字段的标识就不应该再改变。数字标签的范围是[1, 536870911],其中19000~19999是保留数字。

类型

在这里插入图片描述

修饰符

如果一个字段被repeated修饰,则表示它是一个列表类型的字段,如下所示:

message FeatureDatabase {
  repeated Feature features = 1; // 等价于java中的List<Feature> features
  repeated string args = 2 // 等价于java中的List<string> args
}

如果你希望可以预留一些数字标签或者字段可以使用reserved修饰符:

如果你通过删除或者注释所有域,以后的用户可以重用标识号当你重新更新类型的时候。如果你使用旧版本加载相同的.proto文件这会导致严重的问题,包括数据损坏、隐私错误等等。现在有一种确保不会发生这种情况的方法就是指定保留标识符(and/or names, which can also cause issues for JSON serialization不明白什么意思),protocol buffer的编译器会警告未来尝试使用这些域标识符的用户。

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
  string foo = 3 // 编译报错,因为‘foo’已经被标为保留字段
}
默认值
  • string类型的默认值是空字符串

  • bytes类型的默认值是空字节

  • bool类型的默认值是false

  • 数字类型的默认值是0

  • enum类型的默认值是第一个定义的枚举值

  • message类型(对象,如上文的SearchRequest就是message类型)的默认值与 语言 相关

  • repeated修饰的字段默认值是空列表

如果一个字段的值等于默认值(如bool类型的字段设为false),那么它将不会被序列化,这样的设计是为了节省流量。

枚举

每个枚举值有对应的数值,数值不一定是连续的。第一个枚举值的数值必须是0且至少有一个枚举值,否则编译报错。编译后编译器会为你生成对应语言的枚举类。

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}

​ 一个数值可以对应多个枚举值,必须标明option allow_alias = true;

enum EnumAllowingAlias {
  option allow_alias = true;
  UNKNOWN = 0;
  STARTED = 1;
  RUNNING = 1;
}
其他类型的Message定义
message Point {
  int32 latitude = 1;  //对应Java中的 int latiudu;
  int32 longitude = 2; //对应Java中的 int longitude;
}

message Feature {
  string name = 1;	   //对应Java中的 String name; 这里理解成基本数据类型
  Point location = 2;  //对应Java中的 Point location; 这里理解成引用数据类型;
}

message FeatureDatabase {
  repeated Feature feature = 1; // 对应Java中的 List<Featrue> featrue; 引用数据类型
}

一个.proto文件可以定义多个message类型,并且可以按照逻辑进行引用和定义。除此之外,还可以引用其他文件中定义的message;

Import
import "proto/other.proto"; //这样就可以使用other.proto文件中定义的message类型了,这里需要注意的是不能导入不使用的.proto文件

import本身是不具有传递性的,如果希望引入的messgae具有传递效果的话需要在import后使用public进行修饰,见代码:

// 文件a.proto
·····
// 文件b.proto
·····
// 文件c.proto
import "a.proto"; //没有使用 public修饰
import public "b.proto"; //使用 public修饰,会出现继承传递的效果
// 文件文件c_extend.proto
import "c.proto" // 引入c.proto

​ 此时文件c_extend.proto中可以使用c.proto中的messageb.proto中的message,但是不能访问a.proto中的message

嵌套类型

​ 可以在一个message类型中定义另一个message类型,支持无限嵌套,这里可以理解成Java中的内部类。代码示例如下:

message A {
	string name = 1;
	int32 age = 2;
	message B {
		float weight = 1;
		bool sex = 2
	}
	B b_entity = 3;
}

​ 还可以使用ParentType.ChildType的方式调用,类似Java中的静态内部类比如:

// 使用上述定义好的类型
message C {
	A.B a_b_entity = 1;
}
Any类型

Any类型消息允许你在没有指定他们的.proto定义的情况下使用消息作为一个嵌套类型。一个Any类型包括一个可以被序列化bytes类型的任意消息,以及一个URL作为一个全局标识符和解析消息类型。为了使用Any类型,你需要导入import google/protobuf/any.proto

import "google/protobuf/any.proto";

message Response {
	string msg = 1;
	int32 code = 2;
	google.protobuf.Any data = 3; //可以理解成Object
	repeated google.protobuf.Any datas = 4; //可以理解成泛型 List<T> datas;
}

可以通过pack()unpack()(方法名在不同的语言中可能不同)方法装箱/拆箱,以下是Java的例子:

People people = People.newBuilder().setName("proto").setAge(1).build();
// protoc编译后生成的message类
Response r = Response.newBuilder().setData(Any.pack(people)).build();
// 使用Response包装people
System.out.println(r.getData().getTypeUrl());
// type.googleapis.com/example.protobuf.people.People
System.out.println(r.getData().unpack(People.class).getName());
// proto

Any对包装的类型会生成一个URL,默认是type.googleapis.com/packagename.messagename(在Java中可以通过这个特性进行***反射***操作)。

Oneof类型

如果你的消息中有很多可选字段, 并且同时至多一个字段会被设置, 你可以加强这个行为,使用oneof特性节省内存.

Oneof字段就像可选字段, 除了它们会共享内存, 至多一个字段会被设置。 设置其中一个字段会清除其它字段。 你可以使用case()或者WhichOneof() 方法检查哪个oneof字段被设置, 看你使用什么语言了.

//  你可以增加任意类型的字段, 但是不能使用repeated 关键字.
message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

设置oneof会自动清楚其它oneof字段的值. 所以设置多次后,只有最后一次设置的字段有值.

Map(映射)类型

PB中也可以使用map类型(官方并不认为是一种类型,此处称之为类型仅便于理解),绝大多数scalar类型都可以作为key,除了浮点型和bytes,枚举型也不能作为key,value可以是除了map以外的任意类型:

// map<key_type, value_type> map_field = N;
map<string, Project> projects = 3;

map实际是一种语法糖,可以理解成下面:

message MapEntity {
	key_type key = 1;
	value_type value = 2;
}
repeated MapEntity maps = N;

包定义

.proto文件新增一个可选的package声明符,用来防止不同的消息类型有命名冲突。

package routeguide;

service RouteGuide {
	······
}

可以使用全限定名来引用

// 指定包名后,会对生成的代码产生影响,以Java为例,生成的类会以你指定的package作为包名。
message {
	routeguide.RouteGuide rg = 1
	······
}

服务定义

​ 要定义一个服务,你必须在你的 .proto 文件中指定 service

service RouteGuide {
  //A simple RPC. (一个简单的RPC)
  rpc GetFeature(Point) returns (Feature) {} 
  //一个 服务器端流式 RPC , 客户端发送请求到服务器,拿到一个流去读取返回的消息序列。
  //客户端读取返回的流,直到里面没有任何消息。从例子中可以看出,通过在 响应 类型前插入
  //stream 关键字,可以指定一个服务器端的流方法。
  rpc ListFeatures(Rectangle) returns (stream Feature) {}
  //一个 客户端流式 RPC , 客户端写入一个消息序列并将其发送到服务器,同样也是使用流。
  //一旦客户端完成写入消息,它等待服务器完成读取返回它的响应。通过在 请求 类型前指定
  //stream 关键字来指定一个客户端的流方法。
  rpc RecordRoute(stream Point) returns (RouteSummary) {}
  //一个 双向流式 RPC 是双方使用读写流去发送一个消息序列。两个流独立操作,因此客户端
  //和服务器可以以任意喜欢的顺序读写:比如, 服务器可以在写入响应前等待接收所有的客户
  //端消息,或者可以交替的读取和写入消息,或者其他读写的组合。 每个流中的消息顺序被预留。
  //你可以通过在请求和响应前加 stream 关键字去制定方法的类型。
  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}

选项(Option)

在定义.proto文件时能够标注一系列的options。Options并不改变整个文件声明的含义,但却能够影响特定环境下处理方式。完整的可用选项可以在google/protobuf/descriptor.proto找到。

一些选项是文件级别的,意味着它可以作用于最外范围,不包含在任何消息内部、enum或服务定义中。一些选项是消息级别的,意味着它可以用在消息定义的内部。当然有些选项可以作用在域、enum类型、enum值、服务类型及服务方法中。到目前为止,并没有一种有效的选项能作用于所有的类型。

如下就是一些常用的选择:

option java_package = "com.example.foo"; 编译器为以此作为生成的Java类的包名,如果没有该选项,则会以pb的package作为包名。

option java_multiple_files = true; 该选项为true时,生成的Java类将是包级别的,否则会在一个包装类中。

option optimize_for = CODE_SIZE; 该选项会对生成的类产生影响,作用是根据指定的选项对代码进行不同方面的优化。

JSON映射

PB支持和JSON互相转换。如果一个字段不存在JSON数据中或者为null,那么pb中会被赋为该字段的默认值,反之,如果一个字段在PB中是默认值,那么不会写到JSON数据中以节省空间。

在这里插入图片描述
gRPC 官方文档中文版V1.0
Protobuf3语法详解
protobuf3 any用法
Protobuf3学习笔记

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
grpc-go是一款受欢迎的Go语言实现的高性能RPC框架,用于构建分布式系统。它基于Google的开源框架gRPC,并为Go语言提供了相应的接口和库。下面我将对grpc-go源码进行简要剖析。 在grpc-go源码中,最重要的是其核心组件:Transport、Balancer、Resolver和Server。Transport负责网络传输,提供基于TCP、HTTP/2和TLS的通信功能。Balancer用于负载均衡,可根据策略将请求分配到不同的服务节点。Resolver负责服务发现,帮助客户端找到可用的服务实例。Server则是用于接收和处理来自客户端的请求。 在Transport层,grpc-go使用了高效的HTTP/2协议作为底层通信协议。通过HTTP2Transport接口,它可以方便地与底层通信协议进行交互。此外,Transport还利用了框架提供的拦截器机制,可以实现一些额外的功能,比如认证、日志记录等。 在Server端,grpc-go提供了一个灵活的框架,可以通过定义服务接口和实现服务方法来处理请求。它还支持多种传输模式,包括独立的HTTP/2、TCP以及TLS加密等。Server还提供了流式调用和双向流式调用的支持,可以处理大量并行请求。 在Client端,grpc-go提供了方便的接口和功能,用于与服务端进行通信。客户端可以根据服务接口定义的方法来发起请求,并且可以设置超时时间、重试机制等。此外,客户端还支持流式和双向流式调用,可以实现高效的数据交互。 总结来说,grpc-go源码剖析主要集中在核心组件的实现,包括Transport、Balancer、Resolver和Server等。通过这些组件的协同工作,grpc-go能够实现高性能、高效的RPC通信。同时,grpc-go还提供了丰富的功能和接口,方便开发人员使用和扩展。通过理解grpc-go源码,开发人员可以更好地利用这个框架构建高效的分布式系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值