一、基础概念
RPC(Remote Procedure Call)远程过程调⽤,简单来说就是客⼾端在不知道调⽤细节的情况下,调
⽤远程计算机上的某个功能就像调⽤本地功能⼀样,其主要⽬标就是让构建分布式计算(应⽤)更容
易,在提供强⼤的远程调⽤能⼒时不损失本地调⽤的语义简洁性。
| 框架 | 开发者 | 语言支持 | 序列化 | http协议 | 文档 | 编译问题(ubuntu 22.04) | 服务端推送 | 异步调用 | 流式传输(大块数据传输) | 易用性 |
|---|---|---|---|---|---|---|---|---|---|---|
| grpc | C++、Java、Python、Go | pb | 支持 | 文档较为完善,没有中文档 | 主要是下载submodule的问题,没有vpn | 不支持 | 1.异步客户端需要用户自己创建消费线程,使用起来较为麻烦 2.支持异步服务器 | 支持(接口简单易用) | 1.简单易用 2.支持跨语言、跨平台 | |
| brpc | baidu | C++ | pb | 支持 | 文档较为完善,有中文文档 | 编译基本没有问题 | 支持,但是支持的不好,是通过一次长的RPC调用实现的 | 1.通过回调函数支持客户端异步调用 | 支持(接口稍微比grpc原生) | 1.提供简洁的API和友好的使用文档,易于上手 2.在sofa-pbrpc基础上加了一些封装,比如资源管理等,更易用 |
| srpc | sogou | C++ | pb、Thrift | 支持 | 文档略显简单,没有独立网站、都托管在github | 编译基本没有问题 | 不支持 | 1.支持异步客户端 2.通过workflow支持异步服务器 | 不支持 | 专注于异步编程,性能较高,用起来更复杂一些 |
| sofa-pbrpc | baidu | C++、Java | pb | 支持 | 官方文档较少 | 编译存在问题依赖的库版本都比较老 | 不支持 | 1.通过回调函数客户端异步调用 | 不支持 | 1.轻量化 2.接口简单,容易使用 |
brpc 框架介绍
brpc是用C++语言编写的工业级RPC框架,常用于搜索、存储、机器学习、广告、推荐等高性能系统。
核心能力
你可以使用它实现以下功能:
- 搭建单端口多协议服务,同时可访问各类异构服务
- restful http/https、h2/gRPC:brpc内置HTTP实现,使用比libcurl更便捷;支持其他语言通过HTTP/h2+JSON调用基于protobuf定义的协议
- redis、memcached:客户端线程安全,API比官方client更易用
- rtmp/flv/hls:可快速搭建流媒体服务
- thrift:线程安全客户端,使用体验优于官方client
- 百度系列私有协议:baidu_std、streaming_rpc、hulu_pbrpc、sofa_pbrpc、nova_pbrpc、public_pbrpc、ubrpc,以及所有基于nshead封装的协议
- 分布式高可用集群:配套开源braft,基于工业级RAFT算法实现高可靠分布式系统
-
服务端请求处理
Server 支持同步、异步两种模式处理客户端请求
-
多样化客户端调用方式
Client 原生支持同步、异步、半同步调用;支持组合Channels,简化分库、批量并发访问等复杂业务场景
-
内置可视化调试与性能剖析
提供HTTP可视化调试界面,集成CPU、堆内存、锁竞争性能分析工具,方便定位性能问题
-
高性能优势
具备更低请求延迟、更高系统吞吐量
-
高度自定义扩展能力
- 快速接入企业内部自定义私有协议
- 可定制各类核心组件:
- 命名服务:DNS、ZooKeeper、etcd等
- 负载均衡策略:轮询、随机、一致性哈希等
二、类与接口介绍
2.1 ⽇志输出类与接口
包含头⽂件: #include <butil/logging.h>
brpc库内有⼈家⾃⼰的⽇志输出模块(⽆法替换,除⾮修改库内源码中所有的⽇志输出操作后重新编
译库进⾏安装),这⾥我们主要是了解他的⽇志级别,将不需要的⽇志输出给禁⽤掉,避免其运⾏时
的⼤量⽇志输出影响我们的视线。
cpp
namespace logging {
enum LoggingDestination {
LOG_TO_NONE = 0
};
struct BUTIL_EXPORT LoggingSettings {
LoggingSettings();
LoggingDestination logging_dest;
};
bool InitLogging(const LoggingSettings& settings);
}
2.2 服务端类与接口
cpp
namespace brpc {
struct ServerOptions {
//⽆数据传输,则指定时间后关闭连接
int idle_timeout_sec; // Default: -1 (disabled)
// 设置c线程池数量,默认cpu数量
int num_threads; // Default: #cpu-cores
//....
}
enum ServiceOwnership {
//添加服务失败时,服务器将负责删除服务对象
SERVER_OWNS_SERVICE,
//添加服务失败时,服务器也不会删除服务对象
SERVER_DOESNT_OWN_SERVICE
};
class Server {
// 添加服务
// google::protobuf::Service* service告诉服务器请求用哪个函数进行业务处理
int AddService(google::protobuf::Service* service,
ServiceOwnership ownership);
int Start(int port, const ServerOptions* opt);
int Stop(int closewait_ms/*not used anymore*/);
int Join();
//休眠直到ctrl+c按下,或者stop和join服务器
void RunUntilAskedToQuit();
}
class ClosureGuard {
explicit ClosureGuard(google::protobuf::Closure* done);
~ClosureGuard() { if (_done) _done->Run(); }
}
}
- 实际使用
proto
syntax = "proto3";
package cal;
// 开启通用服务
option cc_generic_services = true;
message AddReq
{
int32 num1 = 1;
int32 num2 = 2;
}
message AddResp
{
int32 result = 1;
}
// 加法服务
service CalService
{
rpc Add(AddReq) returns (AddResp);
}
- 生成的处理函数
cpp
virtual void Add(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddResp* response,
::google::protobuf::Closure* done);
- Controller 类
对于服务端,主要用于获取HTTP请求信息,及设置HTTP响应信息
cpp
class Controller : public google::protobuf::RpcController {
// 设置超时时间
void set_timeout_ms(int64_t timeout_ms);
// 最大重试次数
void set_max_retry(int max_retry);
void Reset();
google::protobuf::Message* response();
// HTTP相关
HttpHeader& http_response();
butil::IOBuf& response_attachment();
HttpHeader& http_request();
butil::IOBuf& request_attachment();
// 请求失败及错误原因
bool Failed();
std::string ErrorText();
using AfterRpcRespFnType = std::function<
void(Controller* cntl,
const google::protobuf::Message* req,
const google::protobuf::Message* res)>;
void set_after_rpc_resp_fn(AfterRpcRespFnType&& fn)
}
- inline Closure* NewCallback(void (*function)());函数
很多rpc框架中都支持异步操作,业务处理过程,不一定非要放到这个Add函数内进行,本次请求的结束与否,不由Add函数生命周期决定,当执行done->Run()的时候表示处理完毕
通过一下类实现RAII特性
cpp
class ClosureGuard {
explicit ClosureGuard(google::protobuf::Closure* done);
~ClosureGuard() { if (_done) _done->Run(); }
}
2.3简易服务器搭建
cpp
#include <brpc/server.h>
#include <butil/logging.h>
#include "cal.pb.h"
class CalServiceImpl : public cal::CalService
{
public:
CalServiceImpl(){}
~CalServiceImpl(){}
virtual void Add(::google::protobuf::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddResp* response,
::google::protobuf::Closure* done)
{
brpc::ClosureGuard guard(done); // 确保done在函数结束时被调用
int result = request->num1() + request->num2();
response->set_result(result);
}
// 异步服务端修改
/*{
std::thread thread([=](){
// 异步处理 -- 核心思想,将done->Run()放到线程中执行
brpc::ClosureGuard guard(done);
int result = request->num1() + request->num2();
response->set_result(result);
std::this_thread::sleep_for(std::chrono::seconds(3));
});
thread.detach();// 分离线程,将线程资源释放给系统
std::cout << "------------------------------" << std::endl;
}*/
};
int main(int argc, char* argv[])
{
// 1. 实例化计算服务对象
CalServiceImpl cal_service;
// 2. 定义服务器配置对象
brpc::ServerOptions options;
options.idle_timeout_sec = -1;
// 实例化服务器对象
brpc::Server server;
// 注册服务
int ret = server.AddService(&cal_service, brpc::SERVER_OWNS_SERVICE);
if(ret == -1)
{
std::cerr << "AddService failed, ret = " << ret << std::endl;
return -1;
}
// 启动服务器
ret = server.Start(9000, &options);
if(ret == -1)
{
std::cerr << "Start failed, ret = " << ret << std::endl;
return -1;
}
// 等待服务器退出
server.RunUntilAskedToQuit();
return 0;
}
2.4客⼾端类与接口
CaService Stub:用于客户端进行rpc远程调用 CalService Stub(:google:protobuf:RpcChannel* channel)
class Channel { int Init( const chart server addr and_port, const ChannelOptionst options); )
cpp
struct ChannelOptions {
//请求连接超时时间
int32_t connect_timeout_ms;// Default: 200 (milliseconds)
//rpc请求超时时间
int32_t timeout_ms;// Default: 500 (milliseconds)
//最⼤重试次数
int max_retry;// Default: 3
//序列化协议类型 options.protocol = "baidu_std";
AdaptiveProtocolType protocol;
//....
}
class Channel : public ChannelBase {
//初始化接⼝,成功返回0;
int Init(
const char* server_addr_and_port, //192.168.xx.xx:9000
const ChannelOptions* options);
void CallMethod(const google::protobuf::MethodDescriptor* method,
google::protobuf::RpcController* controller,
const google::protobuf::Message* request,
google::protobuf::Message* response,
google::protobuf::Closure* done);
};
inline ::google::protobuf::Closure* NewCallback(void (*function)());
- 客户端示例代码
cpp
#include <brpc/server.h>
#include <butil/logging.h>
#include "cal.pb.h"
class CalServiceImpl : public cal::CalService
{
public:
CalServiceImpl(){}
~CalServiceImpl(){}
virtual void Add(::google::protobuf::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddResp* response,
::google::protobuf::Closure* done)
{
brpc::ClosureGuard guard(done); // 确保done在函数结束时被调用
int result = request->num1() + request->num2();
response->set_result(result);
}
};
int main(int argc, char* argv[])
{
// 1. 实例化计算服务对象
CalServiceImpl cal_service;
// 2. 定义服务器配置对象
brpc::ServerOptions options;
options.idle_timeout_sec = -1;
// 实例化服务器对象
brpc::Server server;
// 注册服务
int ret = server.AddService(&cal_service, brpc::SERVER_OWNS_SERVICE);
if(ret == -1)
{
std::cerr << "AddService failed, ret = " << ret << std::endl;
return -1;
}
// 启动服务器
ret = server.Start(9000, &options);
if(ret == -1)
{
std::cerr << "Start failed, ret = " << ret << std::endl;
return -1;
}
// 等待服务器退出
server.RunUntilAskedToQuit();
return 0;
}
- 异步客户端
cpp
#include <brpc/channel.h>
#include <butil/logging.h>
#include "cal.pb.h"
void Callback(brpc::Controller* cntl, cal::AddReq* request, cal::AddResp* response)
{
std::unique_ptr<brpc::Controller> cntl_ptr(cntl);
std::unique_ptr<cal::AddReq> request_ptr(request);
std::unique_ptr<cal::AddResp> response_ptr(response);
if (cntl_ptr->Failed())
{
std::cout << "Add failed: " << cntl_ptr->ErrorText() << std::endl;
return;
}
std::cout << "Add success: " << response_ptr->result() << std::endl;
}
// 异步调用
int main(int argc, char* argv[])
{
// 实例化ChannelOptions对象
brpc::ChannelOptions options;
options.protocol = "baidu_std";
// 1. 实例化Channel对象
brpc::Channel channel;
channel.Init("192.168.220.128:9000", &options);
// 实例化CalService对象 -- 客户端调用服务端的方法
cal::CalService_Stub stub(&channel);
brpc::Controller *cntl = new brpc::Controller;
cal::AddReq *request = new cal::AddReq;
cal::AddResp *response = new cal::AddResp;
request->set_num1(1);
request->set_num2(2);
// 异步调用 -- 响应处理函数
// 不支持lambda表达式,只能是普通函数
google::protobuf::Closure *callback = brpc::NewCallback(Callback, cntl, request, response);
stub.Add(cntl, request, response, callback);
std::cout << "-------------------------------" << std::endl;
getchar();
return 0;
}