c++ brpc

本文档详细介绍了如何使用protobuf定义消息格式,并利用BRPC框架在C++中实现RPC服务。从创建proto文件定义请求和响应消息,到生成C++类,再到服务器端实现服务接口和客户端调用,最后通过CMakeLists配置编译,实现了一个简单的RPC通信示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

参考:

(22条消息) 基于protobuf的RPC实现_loop in codes-CSDN博客_protobuf rpc

(22条消息) BRPC系列一、编译及简单示例的运行与分析_likewind1993的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值