syntax = "proto3"; //声明protobuf的版本
package fixbug; //声明了代码所在的包 (对于C++来说就是namespace)
//下面的选项,表示生成service服务类和rpc方法描述, 默认是不生成的
option cc_generic_services = true;
message ResultCode{ //定义返回的错误码
int32 errcode = 1;
bytes errmsg = 2;
}
//常用的数据类型: 数据、列表、映射表
//定义登录请求消息类型 name pwd
message LoginRequest{
bytes name = 1; //等于1表示这是第一个参数,一般string的存储定义为bytes
bytes pwd = 2;
// map<int32,string> test = 3; //映射表类型
}
//定义登录响应消息类型
message LoginResponse{
ResultCode result = 1;
bool success = 3;
}
message GetFriendsListRequest{
uint32 user_id = 1;
}
message User{
bytes name = 1;
uint32 age = 2;
enum Sex{
MAN = 0;
WOMAN = 1;
}
Sex sex = 3;
}
message GetFriendsListResponse{
ResultCode result = 1;
repeated User friend_list = 2; //定义了一个列表数据类型
}
//在protobuf里面怎么定义描述rpc方法的类型---service
service UserService{
rpc Login(LoginRequest) returns(LoginResponse);
rpc GetFriendsList(GetFriendsListRequest) returns(GetFriendsListResponse);
}
关键字service用于定义rpc服务,例如Userservice有两个rpc服务,分别是Login和GetFriendsList, 返回值分别就是上述定义的消息类型。
老样子,先执行protoc xxx.proto --cpp_out=./
生成对应的xxx.pb.cc 和 xxx.pb.h文件。
需要注意的是选项option cc_generic_services = true;
得加上,默认是不生成的。
这边来分析xxx.pb.h文件,简单探究一下rpc调用过程:
//!!!!xxx.pb.h
class UserService : public ::PROTOBUF_NAMESPACE_ID::Service {
protected:
// This class should be treated as an abstract interface.
inline UserService() {};
public:
virtual ~UserService();
typedef UserService_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);
virtual void GetFriendsList(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::GetFriendsListRequest* request,
::fixbug::GetFriendsListResponse* 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(UserService);
};
//!!!!xxx.pb.cc
void UserService::Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::LoginRequest*,
::fixbug::LoginResponse*,
::google::protobuf::Closure* done) {
controller->SetFailed("Method Login() not implemented.");
done->Run();
}
void UserService::GetFriendsList(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::GetFriendsListRequest*,
::fixbug::GetFriendsListResponse*,
::google::protobuf::Closure* done) {
controller->SetFailed("Method GetFriendsList() not implemented.");
done->Run();
}
可以看到一个UserService这个类,这个和message关键字有异曲同工之妙,都是对应的一个类,只不过继承的基类是有所不同的, message消息类型继承于抽象类message, service服务类型继承于抽象类service。此外可以发现在类中实现了在proto文件中定义的两个rpc方法Login和GetFriendsList, 其中两个参数是很熟悉的,request和response,也即这个rpc方法的需要用到的调用参数,类型message基类指针,表示可以接受任意消息类型。
由此可知UserService类是一个服务提供者(Callee)
再来看另一个类
//!!!!xxx.pb.h
class UserService_Stub : public UserService {
public:
UserService_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel);
UserService_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel,
::PROTOBUF_NAMESPACE_ID::Service::ChannelOwnership ownership);
~UserService_Stub();
inline ::PROTOBUF_NAMESPACE_ID::RpcChannel* channel() { return channel_; }
// implements UserService ------------------------------------------
void Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::LoginRequest* request,
::fixbug::LoginResponse* response,
::google::protobuf::Closure* done);
void GetFriendsList(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::GetFriendsListRequest* request,
::fixbug::GetFriendsListResponse* response,
::google::protobuf::Closure* done);
private:
::PROTOBUF_NAMESPACE_ID::RpcChannel* channel_;
bool owns_channel_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UserService_Stub);
//!!!!xxx.pb.cc
void UserService_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);
}
void UserService_Stub::GetFriendsList(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::GetFriendsListRequest* request,
::fixbug::GetFriendsListResponse* response,
::google::protobuf::Closure* done) {
channel_->CallMethod(descriptor()->method(1),
controller, request, response, done);
}
可以看见这个类没有无参构造,有几个RpcChannel*参数传递的构造方法,,不妨看看RpcChannel类:
class PROTOBUF_EXPORT RpcChannel {
public:
inline RpcChannel() {}
virtual ~RpcChannel();
// Call the given method of the remote service. The signature of this
// procedure looks the same as Service::CallMethod(), but the requirements
// are less strict in one important way: the request and response objects
// need not be of any specific class as long as their descriptors are
// method->input_type() and method->output_type().
virtual void CallMethod(const MethodDescriptor* method,
RpcController* controller, const Message* request,
Message* response, Closure* done) = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel);
};
很显然,这是个抽象基类,需要用户自己实现派生类重写基类的CallMethod方法
在之后login和GetFriendsList两个方法基本调用方式一样,都是通过_channel调用CallMethod方法执行对应rpc调用,那么可知UserService_Stub是服务消费者(Caller)
#include <iostream>
#include <string>
#include "user.pb.h"
class UserService : public fixbug::UserServiceRpc //使用rpc服务发布端(rpc服务提供者)
{
public:
bool Login(std::string name, std::string pwd)
{
std::cout<<"doing local service : Login" << std::endl;
std::cout<< "name:" << name << "pwd :" << pwd << std::endl;
}
/**
* 重写基类UserServiceRpc的虚函数 下面这些方法都是框架直接调用的
* caller ===> Login(LoginRequest) ==> transmit ==> callee
* callee ===> Login(LoginRequest) ===> 调用下述的Login方法
* */
void Login(::google::protobuf::RpcController* controller,
const ::fixbug::LoginRequest* request,
::fixbug::LoginResponse* response,
::google::protobuf::Closure* done)
{
//框架给业务上报了请求参数LoginRequest 应用获取相应数据做本地业务
std::string name = request->name();
std::string pwd = request->pwd();
//本地业务
bool res = Login(name, pwd);
//把响应写入 包括错误码、错误消息、返回值
response->set_success(0);
fixbug::ResultCode* rc = response->mutable_result();
rc->set_errcode(0);
rc->set_errmsg("");
//执行回调操作 执行响应对象数据的序列化和网络发送(由框架来完成)
done->Run();
}
};
继承对应的UserServiceRpc服务提供者,重写其基类方法即可。
上面只是发布过程,具体rpc调用待续。。。