提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 大体流程
- 一、编写.proto文件
- 二、服务端搭建
- 三、客户端搭建
-
- 1.构造Channel信道,连接服务器
- 2.构造存根对象,用于rpc调用
- [3.进行rpc调用 --同步调用](#3.进行rpc调用 --同步调用)
- 4.异步调用
前言
使用brpc简单搭建一个echo服务。
大体流程
Rpc调用实现样例:
服务端:
1.创建rpc服务子类继承pb中的EchoService服务类,并实现内部的业务接口逻辑
2.创建rpc服务器类,搭建服务器
- 向服务器类中添加 rpc子服务对象 -- 告诉服务器收到什么请求用哪个接口处理
4.启动服务器
客户端:
1.创建网络通信信道
2.实例化pb中的EchoService Stub类对象
3.发起rpc请求,获取响应进行处理
一、编写.proto文件
定义两个message结构体 代表请求和响应。
定义一个servicer 服务。
使用 protoc --cpp_out./ main生成.cc和.h文件
cpp
syntax="proto3";
package example;
option cc_generic_services = true;
message EchoRequest{
string message = 1;
}
message EchoResponse{
string message = 1;
}
service EchoService{
rpc Echo(EchoRequest) returns (EchoResponse);
}
二、服务端搭建
1.继承EchoService创建一个子类,并重写业务方法
重写的方法就是.proto文件中声明的,这个方法有四个参数,req和resp就不解释了,从req中获取数据,进行业务处理,并把结果写入到resp中。
其中,第一个参数RpcController,这是一个上下文管理类,主要是用来判断请求是否ok。
而第四个参数Closure,在服务器端当响应处理完毕后,需要显示效用这个类中run方法,告诉brpc响应已经处理完了,结果已经写入到resp中,可以给客户端进行响应了。
而为了防止用户忘记调用run,我们可以使用ClosureGuard来管理这个closure对象,他会帮我们调用run方法。
cpp
class EchoServiceImpl : public example::EchoService{
public:
EchoServiceImpl(){}
~EchoServiceImpl(){}
//重写Echo方法
void Echo(google::protobuf::RpcController* controller,
const ::example::EchoRequest* request,
::example::EchoResponse* response,
::google::protobuf::Closure* done){
brpc::ClosureGuard rpc_guard(done);
std::cout << "收到消息:" << request->message() << std::endl;
std::string str = request->message() + "--这是响应!!";
response->set_message(str);
}
};
2.构造服务器对象
构造一个服务器用于网络通信。
cpp
brpc::Server server;
3.向服务器对象中,新增EchoService服务
将Echoservice服务注册进服务器中,当指定的服务请求到来时,调用指定的服务处理函数,这里的SERVER_DOESNT_OWN_SERVICE代表服务添加失败后,服务器不帮忙释放这个对象。
cpp
EchoServiceImpl echo_service;
int ret = server.AddService(&echo_service,brpc::ServiceOwnership::SERVER_DOESNT_OWN_SERVICE);
if (ret == -1) {
std::cout << "添加Rpc服务失败!\n";
return -1;
}
4.启动服务器
启动服务器需要填写监听端口和一个ServerOptions对象.
这个对象主要是设置服务器相关选项,多长时未发送连接相关活动就断开连接,io的线程数量等。
cpp
//4.启动服务器
brpc::ServerOptions options;
options.idle_timeout_sec = -1; //多长时间没有连接相关事件则关闭连接 -1为永不关闭
options.num_threads = 1; //io线程数量
ret = server.Start(8085,&options);
if (ret == -1) {
std::cout << "添加Rpc服务失败!\n";
return -1;
}
server.RunUntilAskedToQuit();// 运行服务器,直到请求退出
三、客户端搭建
1.构造Channel信道,连接服务器
brpc对连接进行了更细粒度的划分,在一个连接上可以建立多个信道,而多个信道他们底层使用的是同一个连接。用户只需要使用channel来与服务器通信,从而抽象了网络细节。
同样的在与服务器建立连接时,也需要传入一个Channeloptions对象,
cpp
brpc::ChannelOptions options;
options.connect_timeout_ms = -1; //连接超时时间
options.timeout_ms = -1; //请求超时时间
options.max_retry = 3; //最大重传次数
options.protocol = "baidu_std"; //网络通信协议
brpc::Channel channel;
int ret = channel.Init("127.0.0.1:8085",&options);
if (ret == -1) {
std::cout << "初始化信道失败!\n";
return -1;
}
2.构造存根对象,用于rpc调用
cpp
example::EchoService_Stub stub(&channel);
3.进行rpc调用 --同步调用
同步调用Echo会阻塞等待响应的返回。
同样的,在客户端的调用中也有四个参数。
第一个参数Controller是用于判断请求是否Ok.
第四个参数就不同了,在服务器中第四个参数是为了告知brpc业务已经处理完毕,可以进行返回响应。客户端的第四个参数主要是为了支持异步调用。
cpp
example::EchoRequest req;
req.set_message("你好,lkm");
example::EchoResponse resp;
brpc::Controller cntl;
stub.Echo(&cntl,&req,&resp,nullptr);
if(cntl.Failed() == true){
std::cout << "Rpc调用失败:" << cntl.ErrorText() << std::endl;
return -1;
}
std::cout << "收到响应: " << resp.message() << std::endl;
4.异步调用
第四个参数是一个Closure对象,它可以设置一个回调函数,当响应返回后调用设置的回调函数进行处理。
需要注意的是,由于Echo现在是异步调用,所有他在调用完后就会立即返回,所以为了防止作用域问题,我们需要把resp和Controller在堆上创建。
cpp
void callback(brpc::Controller* cntl,example::EchoResponse* resp)
{
std::unique_ptr<brpc::Controller> cntl_guard(cntl);
std::unique_ptr<example::EchoResponse> resp_guard(resp);
if (cntl->Failed() == true) {
std::cout << "Rpc调用失败:" << cntl->ErrorText() << std::endl;
return;
}
std::cout << "收到响应: " << resp->message() << std::endl;
}
example::EchoRequest req;
req.set_message("你好,lkm");
example::EchoResponse *resp = new example::EchoResponse();
brpc::Controller *cntl = new brpc::Controller();
google::protobuf::Closure* closure = google::protobuf::NewCallback(callback,cntl,resp); //这里设置回调函数
stub.Echo(cntl,&req,resp,closure);
std::cout << "rpc调用请求已发送" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));