大家好,欢迎来到IT知识分享网。
1 proto文件详解
1.1 message介绍
message:protobuf中定义一个消息类型是通过关键字message字段指定的。消息就算需要传输的数据格式的定义。message关键字类似于C++中的class,Java中的Class,go中的struct。
例如:
在消息中承载的数据分别对应于每一个字段。
其中每个字段都有一个名字和一种类型。
1.2 字段规则
这里我们来定义一下
例子:
定义一个结构
message User{ string username=1; int32 age=2; optional string password=3; // 生成的是指针 repeated string address=4; // 生产的是切片 }
生成一下执行protoc –go_out=./ .\user.proto
生成下面的文件
type User struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` Age int32 `prtobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"` Password *string `protobuf:"bytes,3,opt,name=password,proto3,oneof" json:"password,omiyempty"` Address []string `protobuf:"bytes,4,rep,name=address,proto3" json:"address,omitempty"` }
可以看到Address变成了一个切片。
1.3 字段映射
1.4 标识号
标识号:在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号是[0.2^29-1]范围内的一个整数。
message User{ string username=1; // 位置1 int32 age=2; optional string password=3; repeated string address=4; // 位置4 }
以Person为例,name=1,id=2,email=3,phones=4中的1- 4就是标识号。
1.5 定义服务
如果想要将消息类型用在rpc系统中,可以在.proto文件中定义一个rpc服务接口,protocolbuffer编译器会根据所选择的不同语言生成服务接口代码及存根。
service UserServiceRpc { rpc Login(LoginRequest) returns(LoginResponse); }
上述代表表示,定义了一个RPC服务。
因此完整的.proto文件应该是:
syntax = "proto3"; // 声明了protobuf的版本 package fixbug; // 声明了代码所在的包(对于C++来说是namespace) //定义下面的选项,表示生成service服务类和rpc方法描述,默认不生成 option cc_generic_services = true; message ResultCode//封装一下失败类 { int32 errcode = 1;//表示第1字段 bytes errmsg = 2;//表示第2字段 } // 定义登录请求消息类型 name pwd message LoginRequest { bytes name = 1;//表示第1字段 bytes pwd = 2;//表示第2字段 } // 定义登录响应消息类型 message LoginResponse { ResultCode result = 1;//表示第1字段 bool success = 2;//表示第2字段 } //在protobuf里面怎么定义描述rpc方法的类型 - service service UserServiceRpc { rpc Login(LoginRequest) returns(LoginResponse); }
1.6 UserServiceRpc类 和 UserServiceRpc_stub类
UserServiceRpc类是因为在.proto文件中定义了UserServiceRpc服务而生成的,UserServiceRpc类继承于google::protobuf::Service类
class UserServiceRpc : public ::PROTOBUF_NAMESPACE_ID::Service { protected: // This class should be treated as an abstract interface. inline UserServiceRpc() {}; public: virtual ~UserServiceRpc(); typedef UserServiceRpc_Stub Stub; static const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor* descriptor(); virtual void Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller, const ::fixbug::LoginRequest* request, ::fixbug::LoginResponse* response, ::google::protobuf::Closure* done); // implements Service ---------------------------------------------- const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor* GetDescriptor(); void CallMethod(const ::PROTOBUF_NAMESPACE_ID::MethodDescriptor* method, ::PROTOBUF_NAMESPACE_ID::RpcController* controller, const ::PROTOBUF_NAMESPACE_ID::Message* request, ::PROTOBUF_NAMESPACE_ID::Message* response, ::google::protobuf::Closure* done); const ::PROTOBUF_NAMESPACE_ID::Message& GetRequestPrototype( const ::PROTOBUF_NAMESPACE_ID::MethodDescriptor* method) const; const ::PROTOBUF_NAMESPACE_ID::Message& GetResponsePrototype( const ::PROTOBUF_NAMESPACE_ID::MethodDescriptor* method) const; private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UserServiceRpc); };
Login()虚函数,让派生类重写。我们看到,Login虚函数的名字就是我们定义的rpc服务的方法的名字,其有4个固定的参数。(这块就是重写以后处理服务端的业务逻辑)
UserServiceRpc_stub类继承了UserServiceRpc类,并重写了UserServiceRpc类中的虚函数Login()。
class UserServiceRpc_Stub : public UserServiceRpc { public: UserServiceRpc_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel); UserServiceRpc_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel, ::PROTOBUF_NAMESPACE_ID::Service::ChannelOwnership ownership); ~UserServiceRpc_Stub(); inline ::PROTOBUF_NAMESPACE_ID::RpcChannel* channel() { return channel_; } // implements UserServiceRpc ------------------------------------------ void Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller, const ::fixbug::LoginRequest* request, ::fixbug::LoginResponse* response, ::google::protobuf::Closure* done); private: ::PROTOBUF_NAMESPACE_ID::RpcChannel* channel_; bool owns_channel_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UserServiceRpc_Stub); };
- 其拥有一个RpcChannel类型的指针变量,在构造时需要传入这个值(没有默认构造函数!)。
- 其Login()是继承自基类的函数,其调用了channel的CallMethod函数,Login()的实现如下:
void UserServiceRpc_Stub::Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller, const ::fixbug::LoginRequest* request, ::fixbug::LoginResponse* response, ::google::protobuf::Closure* done) { channel_->CallMethod(descriptor()->method(0), controller, request, response, done); }
1.7 个人理解
service.h
GetDescriptor() 获得service描述符 可以获得service相关的属性
callmethod() 调用service下的method
getrequestPrototype() 根据message描述符 得到请求的message
getresponseprototype() 根据message描述符 得到响应的message
例子
定义一个价格的请求体和相应体,其中price,good代表请求的message所包含的对象。
syntax = "proto3"; option cc_generic_services = true; message makeOrderRequest { int32 price = 1; string goods = 2; } message makeOrderResponse { int32 ret_code = 1; string res_info = 2; string order_id = 3; } service Order { rpc makeOrder(makeOrderRequest) returns (makeOrderResponse); }
使用时首先生成对应的.pb.cc和.pb.h,针对service我们必须重写虚函数makeOrder,这部分主要就是完成你所需要的业务逻辑,起始就是更具请求自动生成相应。
void makeOrder(google::protobuf::RpcController* controller, const ::makeOrderRequest* request, ::makeOrderResponse* response, ::google::protobuf::Closure* done) { if (request->price() < 10) { response->set_ret_code(-1); response->set_res_info("short balance"); return; } response->set_order_id("21111"); }
在一个典型的RPC调用过程中,客户端首先与服务器建立连接。然后,客户端将请求编码成特定的格式,通常是使用一种序列化协议(如Protobuf等),将请求数据打包成二进制数据。接着,客户端通过网络发送请求给服务器。
服务器收到请求后,会进行解码操作,将接收到的二进制数据还原为原始的请求数据结构。然后,服务器根据请求中指定的方法名(通常是通过反射机制获取方法名)调用相应的方法(callmethod)(callmethod() 调用service下的method)。方法执行完成后,服务器将返回的结果进行编码(序列化),再次打包成二进制数据,并通过网络发送给客户端。
客户端接收到服务器返回的响应后,进行解码操作,将二进制数据还原为原始的响应数据结构。最后,客户端可以从响应中获取到所需的结果数据。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/98920.html

