1.书写protoc2文件:
protoc文件是谷歌出品的网络通讯规范,他被brpc用作底层消息结构模块。
按照自带的example,echo_c++中的protoc文件定义,我们可以先复制到一个自己的目录。proto文件定义了客户端和服务端的请求、相应的消息格式。
syntax="proto2"; //指定proto版本
option cc_generic_services = true; //设置为生成C++类
message MsgClient{ //客户端发送的消息
required string msg=1; //必须要有的字段:required
};
message MsgServer{ //服务器回复的消息
required string msg=1; //必须要有的字段:required
}
/*
定义服务(Service)
如果想要将消息类型用在RPC(远程方法调用)系统中,
可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据
所选择的不同语言生成服务接口代码及存根。如,想要定义一个RPC服务并具有一个方法
,该方法能够接收 SearchRequest并返回一个SearchResponse,此时可以在.proto
文件中进行如下定义:
*/
//定义一个rpc服务,有一个名为Answer的函数
//接受一个来自客户端的消息MsgClient,返回一个服务器相应消息MsgServer
service FirstService{ //自己的第一个brpc服务
rpc Answer(MsgClient) returns (MsgServer);
};
定义好proto文件后,即可生成相应的C++类文件:
protoc --cpp_out=./ ./msg.proto
我们打开msg.pb.h,里面可以找到一个名为:FirstService的类,其中有一个virtual方法,Answer,这就是我们刚刚在service段中定义的rpc函数。我们要做的,就是在服务器server.cpp实现这个Answer函数,在其中处理相关的信息
server.cpp:
#include<iostream>
#include<brpc/server.h>
#include"msg.pb.h"
using namespace std;
class AnswerImpl : public FirstService{
public:
AnswerImpl(){}//构造函数
virtual ~AnswerImpl(){}//析构函数
virtual void Answer(::google::protobuf::RpcController* controller,
const ::MsgClient* request,
::MsgServer* response,
::google::protobuf::Closure* done){
// 这个对象确保在return时自动调用done->Run()
brpc::ClosureGuard done_guard(done);
brpc::Controller* cntl = static_cast<brpc::Controller*>(controller);
//LOG是glog的功能,也可以自己选择其他的日志库or输出流
LOG(INFO) << "收到消息[log_id=" << cntl->log_id()
<< "] 从 " << cntl->remote_side()
<< " 到 " << cntl->local_side()
<< ": " << request->msg()
<< " (附加消息=" << cntl->request_attachment() << ")";
// 填写response
response->set_msg(request->msg()); //把客户端发送的消息原封不动发送回去
//这个msg正是msg.proto中定义的msg结构
}
};
int main(){
brpc::Server server;
AnswerImpl aImpl; //实例化你的类
//默认构造后的Server不包含任何服务,也不会对外提供服务,仅仅是一个对象。
//通过如下方法插入你的Service实例。
//若ownership参数为SERVER_OWNS_SERVICE,
//Server在析构时会一并删除Service,否则应设为SERVER_DOESNT_OWN_SERVICE。
if (server.AddService(&aImpl,
brpc::SERVER_DOESNT_OWN_SERVICE) != 0) {
LOG(ERROR) << "service添加失败!";
return -1;
}
//开始启动服务器
brpc::ServerOptions options;
options.idle_timeout_sec = 200; //客户端200s没动作则断开链接
if (server.Start(8000, &options) != 0) { //启动服务器在8000端口
LOG(ERROR) << "无法启动服务器";
return -1;
}
//等待到ctrl+c按下,否则就一直启动服务
server.RunUntilAskedToQuit();
return 0;
}
CMakeLists.txt。
#指定最低版本
cmake_minimum_required(VERSION 2.6)
#设置工程名
project(brpcstudy)
#引入thread库
include(FindThreads)
#cmake自带搜索protobuf,把protobuf相关库文件包含进来
include(FindProtobuf)
message(WARNING "0: ${Protobuf_INCLUDE_DIRS} ${Protobuf_LIBRARIES}")
protobuf_generate_cpp(PROTO_SRC PROTO_HEADER msg.proto)
message(WARNING "1: ${PROTO_SRC} ${PROTO_HEADER}")
#搜索brpc头文件和库文件路径
find_path(BRPC_INCLUDE_PATH NAMES brpc/server.h PATHS /home/keda/demo/brpcDemo/include/)
message(WARNING "2: ${BRPC_INCLUDE_PATH}")
include_directories(${BRPC_INCLUDE_PATH})
find_library(BRPC_LIB NAMES libbrpc.a PATHS /home/keda/demo/brpcDemo/lib/)
message(WARNING "3: ${BRPC_LIB}")
#搜索gflags相关依赖,这个主要是解析命令行参数的,用不到可以不搜索
find_path(GFLAGS_INCLUDE_PATH gflags/gflags.h)
message(WARNING "4: ${GFLAGS_INCLUDE_PATH}")
find_library(GFLAGS_LIBRARY NAMES gflags libgflags)
message(WARNING "5: ${GFLAGS_LIBRARY}")
include_directories(${GFLAGS_INCLUDE_PATH})
#搜索leveldb相关依赖
find_path(LEVELDB_INCLUDE_PATH NAMES leveldb/db.h)
find_library(LEVELDB_LIB NAMES leveldb)
include_directories(${LEVELDB_INCLUDE_PATH})
#brpc必须要openssl的lib
find_package(OpenSSL)
#使用server.cpp ${PROTO_SRC} ${PROTO_HEADER}生成可执行文件msg_server
add_executable(msg_server server.cpp ${PROTO_SRC} ${PROTO_HEADER})
#使用client.cpp ${PROTO_SRC} ${PROTO_HEADER}生成可执行文件msg_client
add_executable(msg_client client.cpp ${PROTO_SRC} ${PROTO_HEADER})
#添加BRPC所需要的lib
set(DYNAMIC_LIB
${CMAKE_THREAD_LIBS_INIT}
${GFLAGS_LIBRARY}
${PROTOBUF_LIBRARIES}
${LEVELDB_LIB}
${OPENSSL_CRYPTO_LIBRARY}
${OPENSSL_SSL_LIBRARY}
dl
)
#将目标文件与库文件进行链接
target_link_libraries(msg_server ${BRPC_LIB} ${DYNAMIC_LIB})
target_link_libraries(msg_client ${BRPC_LIB} ${DYNAMIC_LIB})
client.cpp:
#include<iostream>
#include<brpc/channel.h> //客户端引用的是channel.h,和服务器的server.h不一样
#include"msg.pb.h"
using namespace std;
int main(){
brpc::Channel channel;//新建一个channel和服务器通信
//填充这个channel的相关参数
brpc::ChannelOptions options;
options.protocol="baidu_std";//rpc协议,默认是百度std
options.max_retry=3;//当与服务器丢失连接时重试的最大次数
if (channel.Init("0.0.0.0:8000", "", &options) != 0) {
LOG(ERROR) << "初始化channel失败";
return -1;
}
//通常,您不应直接调用Channel,而应构造
//包装它的存根服务。 存根也可以由所有线程共享。
//每一个你生成的类都有一个"名字_Stub"的包装对象,可以在xxx.pb.h里面搜索
FirstService_Stub stub(&channel);
int log_id=0;
//brpc的每条消息都有一个log_id用来指示这个客户端自连接以来发送消息的条数,可以手动设置
while(1){
::MsgClient request; //客户端的“对象”
::MsgServer response; //服务器的“对象”
brpc::Controller cntl; //控制句柄
cntl.set_log_id(log_id++); //设置消息的id
string msg;
LOG(INFO)<<"请输入要发送到服务器的消息:";
cin>>msg;
request.set_msg(msg.c_str());//设置消息
//因为`done'(最后一个参数)为NULL,所以此函数一直等到
//响应返回或发生错误(包括超时)。
stub.Answer(&cntl,&request,&response,nullptr);
if (!cntl.Failed()) {
LOG(INFO) << "收到服务器 " << cntl.remote_side()
<< " 发送到 " << cntl.local_side()
<< ": " << response.msg()
<< " 延时=" << cntl.latency_us() << "us";
} else {
LOG(WARNING) << cntl.ErrorText();
}
}
return 0;
}
mkdir build
cd build
cmake ..
make
测试:
./msg_client,./msg_server
参考: