BRpc 介绍及使用
- [一. Rpc 介绍](#一. Rpc 介绍)
- [二. BRpc 介绍](#二. BRpc 介绍)
- [三. BRpc 安装](#三. BRpc 安装)
- [四. BRpc 类与接口](#四. BRpc 类与接口)
-
- [1. 日志输出类与接口](#1. 日志输出类与接口)
- [2. ProtoBuf 类与接口](#2. ProtoBuf 类与接口)
- [3. 服务端类与接口](#3. 服务端类与接口)
- [4. HTTP 类与接口](#4. HTTP 类与接口)
- [5. 客户端类与接口](#5. 客户端类与接口)
- [五. BRpc 使用样例](#五. BRpc 使用样例)
-
- [1. 目录结构](#1. 目录结构)
- [2. 项目构建](#2. 项目构建)
- [3. 代码实现](#3. 代码实现)
-
- [3.1 .proto 文件](#3.1 .proto 文件)
- [3.2 同步 RPC 客户端、同步 RPC 服务端](#3.2 同步 RPC 客户端、同步 RPC 服务端)
- [3.3 异步 RPC 客户端、异步 RPC 服务端](#3.3 异步 RPC 客户端、异步 RPC 服务端)
- [3.4 HTTP 客户端、HTTP 服务端](#3.4 HTTP 客户端、HTTP 服务端)
- [六. BRpc 封装](#六. BRpc 封装)
-
- [1. 设计与实现](#1. 设计与实现)
-
- [1.1 目录结构](#1.1 目录结构)
- [1.2 代码实现](#1.2 代码实现)
- [2. 使用样例](#2. 使用样例)
-
- [2.1 目录结构](#2.1 目录结构)
- [2.2 项目构建](#2.2 项目构建)
- [2.3 代码实现](#2.3 代码实现)
一. Rpc 介绍
RPC (Remote Procedure Call) 远程过程调用,简单来说就是客户端在不知道调用细节的情况下,调用远程计算机上的某个功能就像调用本地功能一样,其主要目标就是让构建分布式计算 (应用) 更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。
二. BRpc 介绍
- BRPC (Baidu RPC) 是百度开源的一款高性能 C++ RPC 框架,主要用于构建大规模分布式系统中的服务通信。它以低延迟、高吞吐为设计目标,广泛应用于对性能和稳定性要求极高的后端场景。
- BRPC 支持多种通信协议,包括百度自研 RPC、HTTP/HTTPS、gRPC、Thrift、Redis 协议等,同一服务可同时对外提供多种接口。框架内置连接复用、超时控制、重试、负载均衡、限流和监控统计等工程能力,运维友好。BRPC 还引入了轻量级用户态线程 bthread,能够高效处理大量并发 I/O 请求。整体而言,BRPC 是一款偏工程化、成熟稳定、适合 C++ 高并发服务的 RPC 框架。
三. BRpc 安装
先安装依赖
bash
sudo apt-get install -y git g++ make libssl-dev libprotobuf-dev libprotoc-dev protobuf-compiler libleveldb-dev
安装 brpc
bash
# 下载源码
git clone https://github.com/apache/brpc.git
# 切换目录
cd brpc/
mkdir build
cd build
# 生成 Makefile
cmake -DCMAKE_INSTALL_PREFIX=/usr .. && cmake --build . -j6
# 编译代码
make
# 安装
sudo make install
由于在环境搭建中已经安装了 BRpc,这里不需要再次安装了。
四. BRpc 类与接口
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. ProtoBuf 类与接口
cpp
namespace google {
namespace protobuf {
// 闭包类
class PROTOBUF_EXPORT Closure {
public:
Closure() {} // 构造函数
virtual ~Closure(); // 析构函数
virtual void Run() = 0; // 在 RPC 调用完成时调用,表明 RPC 调用已完成
};
// PRC 控制器类
class PROTOBUF_EXPORT RpcController {
public:
bool Failed(); // 判断 RPC 调用是否失败
std::string ErrorText() ; // 获取 RPC 调用发生错误的文本
};
// 异步调用所使用的回调函数类
template <typename F, typename... Args>
google::protobuf::Closure* NewCallback(F&& f, Args&&... args) {...}
}
}
3. 服务端类与接口
cpp
namespace brpc {
// 服务器选项
struct ServerOptions {
int idle_timeout_sec; // 无数据传输,则指定时间后关闭连接,默认值为60秒
int num_threads; // 线程数,默认值为10
//....
}
// 服务所有权
enum ServiceOwnership {
SERVER_OWNS_SERVICE, // 添加服务失败时,服务器会删除服务对象
SERVER_DOESNT_OWN_SERVICE // 添加服务失败时,服务器不会删除服务对象
};
// 服务器类
class Server {
// service 服务对象,告诉服务器哪个请求应该用哪个函数进行处理
// ownership 服务所有权,决定服务器是否在添加服务失败时删除服务对象
int AddService(google::protobuf::Service* service, ServiceOwnership ownership); // 添加服务
int Start(int port, const ServerOptions* opt); // 启动服务器
int Stop(int closewait_ms); // 停止服务器
int Join(); // 等待服务器线程退出
void RunUntilAskedToQuit(); // 运行直到被要求退出
}
// 闭包守卫类
class ClosureGuard {
explicit ClosureGuard(google::protobuf::Closure* done); // 构造函数
~ClosureGuard() { if (_done) _done->Run(); } // 析构函数:如果存在闭包,则运行闭包,表明 RPC 请求处理完成
}
}
4. HTTP 类与接口
cpp
// URI 类:用于解析和操作 URI
class URI {
typedef butil::FlatMap<std::string, std::string> QueryMap;
typedef QueryMap::const_iterator QueryIterator;
int SetHttpURL(const std::string& url); // 设置 HTTP URL
void set_path(const std::string& path); // 设置路径
void set_host(const std::string& host); // 设置主机
void set_port(int port); // 设置端口
void SetHostAndPort(const std::string& host_and_optional_port); // 设置主机和端口
size_t RemoveQuery(const char* key); // 删除查询参数
const std::string& host() const; // 获取主机
int port() const; // 获取端口
const std::string& path() const; // 获取路径
const std::string& user_info() const; // 获取用户信息
const std::string& query() const; // 获取查询参数
const std::string* GetQuery(const std::string& key) // 获取查询参数值
void SetQuery(const std::string& key, const std::string& value); // 设置查询参数值
QueryIterator QueryBegin() const; // 查询参数迭代器 begin
QueryIterator QueryEnd() const; // 查询参数迭代器 end
size_t QueryCount() const; // 查询参数数量
};
// HTTP 方法枚举
enum HttpMethod {
HTTP_METHOD_DELETE = 0, // DELETE 方法
HTTP_METHOD_GET = 1, // GET 方法
HTTP_METHOD_HEAD = 2, // HEAD 方法
HTTP_METHOD_POST = 3, // POST 方法
HTTP_METHOD_PUT = 4, // PUT 方法
};
const char *HttpMethod2Str(HttpMethod http_method); // HTTP 方法枚举转换为字符串
bool Str2HttpMethod(const char* method_str, HttpMethod* method); // 字符串转换为 HTTP 方法枚举
static const int HTTP_STATUS_OK = 200; // HTTP 状态码:成功
static const int HTTP_STATUS_BAD_REQUEST = 400; // HTTP 状态码:错误请求
static const int HTTP_STATUS_UNAUTHORIZED = 401; // HTTP 状态码:未授权
static const int HTTP_STATUS_FORBIDDEN = 403; // HTTP 状态码:禁止访问
static const int HTTP_STATUS_NOT_FOUND = 404; // HTTP 状态码:未找到
static const int HTTP_STATUS_METHOD_NOT_ALLOWED = 405; // HTTP 状态码:方法不允许
static const int HTTP_STATUS_INTERNAL_SERVER_ERROR = 500; // HTTP 状态码:内部服务器错误
// HTTP 头类:用于解析和操作 HTTP 头
class HttpHeader {
const std::string& content_type() const; // 获取内容类型
void set_content_type(const std::string& type); // 设置内容类型
const std::string* GetHeader(const std::string& key) const; // 获取 HTTP 头值
void SetHeader(const std::string& key, const std::string& value); // 设置 HTTP 头值
const URI& uri() const; // 获取 URI
HttpMethod method() const; // 获取 HTTP 方法
void set_method(const HttpMethod method); // 设置 HTTP 方法
int status_code() const; // 获取 HTTP 状态码
void set_status_code(int status_code); // 设置 HTTP 状态码
}
// Controller 类:用于保存 RPC 请求是否成功的状态
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(); // 获取响应消息指针
HttpHeader& http_response(); // 获取 HTTP 响应头引用
butil::IOBuf& response_attachment(); // 获取响应附件 IOBuf 引用
HttpHeader& http_request(); // 获取 HTTP 请求头引用
butil::IOBuf& request_attachment(); // 获取请求附件 IOBuf 引用
bool Failed(); // 判断 RPC 请求是否失败
std::string ErrorText(); // 获取 RPC 请求失败的错误信息
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); // 设置 RPC 响应回调函数
}
5. 客户端类与接口
cpp
namespace brpc {
// 协议类型
enum ProtocolType : int {
PROTOCOL_UNKNOWN = 0,
PROTOCOL_BAIDU_STD = 1,
PROTOCOL_STREAMING_RPC = 2,
PROTOCOL_HULU_PBRPC = 3,
PROTOCOL_SOFA_PBRPC = 4,
PROTOCOL_RTMP = 5,
PROTOCOL_THRIFT = 6,
PROTOCOL_HTTP = 7,
PROTOCOL_PUBLIC_PBRPC = 8,
PROTOCOL_NOVA_PBRPC = 9,
PROTOCOL_REDIS = 10,
PROTOCOL_NSHEAD_CLIENT = 11,
PROTOCOL_NSHEAD = 12,
PROTOCOL_HADOOP_RPC = 13,
PROTOCOL_HADOOP_SERVER_RPC = 14,
PROTOCOL_MONGO = 15,
PROTOCOL_UBRPC_COMPACK = 16,
PROTOCOL_DIDX_CLIENT = 17,
PROTOCOL_MEMCACHE = 18,
PROTOCOL_ITP = 19,
PROTOCOL_NSHEAD_MCPACK = 20,
PROTOCOL_DISP_IDL = 21,
PROTOCOL_ERSDA_CLIENT = 22,
PROTOCOL_UBRPC_MCPACK2 = 23,
PROTOCOL_CDS_AGENT = 24,
PROTOCOL_ESP = 25,
PROTOCOL_H2 = 26
};
// 通道选项
struct ChannelOptions {
int32_t connect_timeout_ms; // 请求连接超时时间,默认值为200毫秒
int32_t timeout_ms; // rpc请求超时时间,默认值为500毫秒
int max_retry; // 最大重试次数,默认值为3次
AdaptiveProtocolType protocol; // 序列化协议类型,默认值为baidu_std
//....
}
// 通道类
class Channel : public ChannelBase {
// 初始化通道
int Init(const char* server_addr_and_port, 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);
};
}
// 异步调用所使用的回调函数类
template <typename F, typename... Args>
google::protobuf::Closure* NewCallback(F&& f, Args&&... args) {...}
五. BRpc 使用样例
1. 目录结构
bash
brpc/
|-- async_client.cc
|-- async_server.cc
|-- cal.pb.cc
|-- cal.pb.h
|-- cal.proto
|-- http_client.cc
|-- http_server.cc
|-- makefile
|-- sync_client.cc
|-- sync_server.cc
2. 项目构建
bash
# makefile
all: sync_server sync_client async_server async_client http_server http_client
sync_server: sync_server.cc cal.pb.cc
g++ -o $@ $^ -std=c++17 -lbrpc -lleveldb -lprotobuf -lpthread -ldl -lssl -lcrypto -lgflags
sync_client: sync_client.cc cal.pb.cc
g++ -o $@ $^ -std=c++17 -lbrpc -lleveldb -lprotobuf -lpthread -ldl -lssl -lcrypto -lgflags
async_server: async_server.cc cal.pb.cc
g++ -o $@ $^ -std=c++17 -lbrpc -lleveldb -lprotobuf -lpthread -ldl -lssl -lcrypto -lgflags
async_client: async_client.cc cal.pb.cc
g++ -o $@ $^ -std=c++17 -lbrpc -lleveldb -lprotobuf -lpthread -ldl -lssl -lcrypto -lgflags
http_server: http_server.cc cal.pb.cc
g++ -o $@ $^ -std=c++17 -lbrpc -lleveldb -lprotobuf -lpthread -ldl -lssl -lcrypto -lgflags
http_client: http_client.cc cal.pb.cc
g++ -o $@ $^ -std=c++17 -lbrpc -lleveldb -lprotobuf -lpthread -ldl -lssl -lcrypto -lgflags
%.pb.cc: %.proto
protoc --cpp_out=./ $^
.PHONY: clean
clean:
rm -f sync_server sync_client async_server async_client http_server http_client
3. 代码实现
3.1 .proto 文件
cpp
// cal.proto
syntax = "proto3"; // 描述语法版本
package cal; // 声明包名称(C++对应的是命名空间)
option cc_generic_services = true; // 默认情况 protoc 命令并不会针对 service 服务生成对应 rpc 代码,需要开启选项才会进行生成
// 定义 RPC 类型
message AddReq {
int32 num1 = 1;
int32 num2 = 2;
}
message AddRsp {
int32 result = 1;
}
// 定义 HTTP 类型
message HelloReq {}
message HelloRsp {}
// 定义 RPC 服务
service CalService {
rpc Add(AddReq) returns (AddRsp);
rpc Hello(HelloReq) returns (HelloRsp);
}
执行 make 命令,生产 cal.pb.cc 和 cal.pb.h 文件,如下:

cpp
// cal.pb.h
namespace cal {
// AddReq 请求对象
class AddReq PROTOBUF_FINAL : public ::PROTOBUF_NAMESPACE_ID::Message {
public:
enum : int {
kNum1FieldNumber = 1,
kNum2FieldNumber = 2,
};
// int32 num1 = 1;
void clear_num1(); // 清除 num1 字段的值
::PROTOBUF_NAMESPACE_ID::int32 num1() const; // 获取 num1 字段的值
void set_num1(::PROTOBUF_NAMESPACE_ID::int32 value); // 设置 num1 字段的值
// int32 num2 = 2;
void clear_num2(); // 清除 num2 字段的值
::PROTOBUF_NAMESPACE_ID::int32 num2() const; // 获取 num2 字段的值
void set_num2(::PROTOBUF_NAMESPACE_ID::int32 value); // 设置 num2 字段的值
};
// AddRsp 响应对象
class AddRsp PROTOBUF_FINAL : public ::PROTOBUF_NAMESPACE_ID::Message {
public:
enum : int {
kResultFieldNumber = 1,
};
// int32 result = 1;
void clear_result(); // 清除 result 字段的值
::PROTOBUF_NAMESPACE_ID::int32 result() const; // 获取 result 字段的值
void set_result(::PROTOBUF_NAMESPACE_ID::int32 value); // 设置 result 字段的值
};
// HelloReq 请求对象
class HelloReq PROTOBUF_FINAL : public ::PROTOBUF_NAMESPACE_ID::Message {};
// HelloRsp 响应对象
class HelloRsp PROTOBUF_FINAL : public ::PROTOBUF_NAMESPACE_ID::Message {};
// 服务器使用的类
class CalService : public ::PROTOBUF_NAMESPACE_ID::Service {
public:
// controller 用于获取 HTTP 请求信息以及设置 HTTP 响应信息
// request 为 RPC 请求参数
// response 为 RPC 响应参数
// done 当 RPC 调用完成时,服务器会调用 done->Run(),表示 RPC 调用完成
virtual void Add(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddRsp* response,
::google::protobuf::Closure* done);
virtual void Hello(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::cal::HelloReq* request,
::cal::HelloRsp* response,
::google::protobuf::Closure* done);
// 调用方法
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);
};
// 客户端使用的类
class CalService_Stub : public CalService {
public:
// channel 为 RPC 通道指针,ownership 为通道所有权
CalService_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel);
CalService_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel, ::PROTOBUF_NAMESPACE_ID::Service::ChannelOwnership ownership);
// controller 用于设置 HTTP 请求信息/获取 RPC 请求是否成功的状态
// request 为 RPC 请求参数
// response 为 RPC 响应参数
// done 取值为 NULL 时,客户端同步 RPC 调用;取值不为 NULL 时,客户端异步 RPC 调用
void Add(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddRsp* response,
::google::protobuf::Closure* done);
void Hello(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::cal::HelloReq* request,
::cal::HelloRsp* response,
::google::protobuf::Closure* done);
};
}
3.2 同步 RPC 客户端、同步 RPC 服务端
同步调用是指 client 会阻塞收到 server 端的响应或发生错误。
cpp
// sync_client.cc
#include <brpc/channel.h>
#include "cal.pb.h"
int main()
{
// 1.实例化 ChannelOptions 信道选项对象
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_BAIDU_STD; // 设置为百度标准 RPC 协议
// 2.实例化 Channel 信道对象
brpc::Channel channel;
channel.Init("192.168.174.128:9000", &options);
// 3.实例化 CalService_Stub 客户端对象,用于发起 RPC 请求
cal::CalService_Stub stub(&channel);
// 4.实例化 Controller 控制器对象,用于保存 RPC 请求是否成功的状态
brpc::Controller cntl;
// 5.实例化 AddReq 请求对象、AddRsp 响应对象
cal::AddReq req;
cal::AddRsp rsp;
req.set_num1(10);
req.set_num2(20);
// 6.发起 RPC 同步请求
stub.Add(&cntl, &req, &rsp, nullptr); // 阻塞,等待 RPC 响应返回
// 7.判断 RPC 请求是否失败
if (cntl.Failed()) {
std::cout << "RPC 同步请求失败,原因:" << cntl.ErrorText() << std::endl;
return -1;
}
// 8.打印 RPC 响应结果
std::cout << "RPC 同步请求结果:" << rsp.result() << std::endl;
return 0;
}
cpp
// sync_server.cc
#include <butil/logging.h>
#include <brpc/server.h>
#include "cal.pb.h"
// 创建 CalServiceImpl 类来实现 CalService 服务
class CalServiceImpl : public cal::CalService {
public:
CalServiceImpl() {}
~CalServiceImpl() {}
// 重写 Add 方法
virtual void Add(::google::protobuf::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddRsp* response,
::google::protobuf::Closure* done) override {
// 当 done_guard 被释放时执行 done->Run() 表明 RPC 同步请求完成
brpc::ClosureGuard done_guard(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; // 设置为 -1 表示不超时
// 3.实例化服务器对象
brpc::Server server;
// 4.向服务器添加计算服务
int ret = server.AddService(&cal_service, brpc::SERVER_DOESNT_OWN_SERVICE); // 服务对象不由服务器管理释放
if (ret == -1) {
std::cerr << "添加计算服务失败" << std::endl;
return -1;
}
// 5.启动服务器
ret = server.Start(9000, &options);
if (ret == -1) {
std::cerr << "启动服务器失败" << std::endl;
return -1;
}
// 6.等待服务器退出
server.RunUntilAskedToQuit();
return 0;
}

3.3 异步 RPC 客户端、异步 RPC 服务端
异步调用是指 client 注册一个响应处理回调函数,当调用一个 RPC 接口时立即返回,不会阻塞等待响应,当 server 返回响应时会调用传入的回调函数处理响应。
cpp
// async_client.cc
#include <brpc/channel.h>
#include "cal.pb.h"
// 当 RPC 异步调用完成时(也就是执行done->Run()时),会调用回调函数 Callback 将结果输出
void Callback(brpc::Controller* cntl, cal::AddReq* req, cal::AddRsp* rsp) {
std::unique_ptr<brpc::Controller> cntl_guard(cntl);
std::unique_ptr<cal::AddReq> req_guard(req);
std::unique_ptr<cal::AddRsp> rsp_guard(rsp);
if (cntl->Failed()) {
std::cout << "RPC 异步请求失败,原因:" << cntl->ErrorText() << std::endl;
return;
}
std::cout << "RPC 异步请求结果:" << rsp->result() << std::endl;
}
int main()
{
// 1.实例化 ChannelOptions 信道选项对象
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_BAIDU_STD; // 设置为百度标准 RPC 协议
// 2.实例化 Channel 信道对象
brpc::Channel channel;
channel.Init("192.168.174.128:9000", &options);
// 3.实例化 CalService_Stub 客户端对象,用于发起 RPC 请求
cal::CalService_Stub stub(&channel);
// 4.实例化 Controller 控制器对象,用于保存 RPC 请求是否成功的状态
brpc::Controller* cntl = new brpc::Controller();
cntl->set_timeout_ms(4000); // 设置 RPC 异步请求超时时间为 4000 毫秒
// 5.实例化 AddReq 请求对象、AddRsp 响应对象
cal::AddReq* req = new cal::AddReq();
cal::AddRsp* rsp = new cal::AddRsp();
req->set_num1(10);
req->set_num2(20);
// 6.创建异步回调函数
google::protobuf::Closure* closure = brpc::NewCallback(Callback, cntl, req, rsp);
// 7.发起 RPC 异步请求
stub.Add(cntl, req, rsp, closure); // 非阻塞,立即返回
std::cout << "--------------------" << std::endl;
getchar(); // 等待用户输入,防止程序退出
return 0;
}
cpp
// async_server.cc
#include <thread>
#include <butil/logging.h>
#include <brpc/server.h>
#include "cal.pb.h"
// 创建 CalServiceImpl 类来实现 CalService 服务
class CalServiceImpl : public cal::CalService {
public:
CalServiceImpl() {}
~CalServiceImpl() {}
// 重写 Add 方法
virtual void Add(::google::protobuf::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddRsp* response,
::google::protobuf::Closure* done) override {
std::thread th([=](){
// 当 done_guard 被释放时执行 done->Run() 表明 RPC 异步请求完成
brpc::ClosureGuard done_guard(done);
std::this_thread::sleep_for(std::chrono::seconds(3)); // 模拟 RPC 调用耗时 3 秒
int result = request->num1() + request->num2();
response->set_result(result);
});
th.detach(); // 分离线程,线程结束后由系统回收资源,创建者线程不再关心它
std::cout << "====================" << std::endl;
}
};
int main(int argc, char* argv[])
{
// 1.实例化计算服务对象
CalServiceImpl cal_service;
// 2.定义服务器配置对象
brpc::ServerOptions options;
options.idle_timeout_sec = -1; // 设置为 -1 表示不超时
// 3.实例化服务器对象
brpc::Server server;
// 4.向服务器添加计算服务
int ret = server.AddService(&cal_service, brpc::SERVER_DOESNT_OWN_SERVICE); // 服务对象不由服务器管理释放
if (ret == -1) {
std::cerr << "添加计算服务失败" << std::endl;
return -1;
}
// 5.启动服务器
ret = server.Start(9000, &options);
if (ret == -1) {
std::cerr << "启动服务器失败" << std::endl;
return -1;
}
// 6.等待服务器退出
server.RunUntilAskedToQuit();
return 0;
}

总结:
- 同步客户端 + 同步服务器:客户端发起同步 RPC 请求 (阻塞) → 服务器在 worker 线程中同步执行 → 执行完成立即 done->Run() 发送响应 → 客户端解除阻塞,RPC 结束。
- 同步客户端 + 异步服务器:客户端发起同步 RPC 请求 (阻塞) → 服务器 worker 线程收到请求 → 启动异步线程并立刻 return → 异步线程完成业务逻辑 → 调用 done->Run() 发送响应 → 客户端解除阻塞,RPC 结束。
- 异步客户端 + 同步服务器:客户端发起异步 RPC 请求 (非阻塞,立即返回) → 服务器在 worker 线程中同步执行 → 执行完成立即 done->Run() 发送响应 → 客户端 IO/bthread (内部框架线程) 收到响应后,触发回调函数获取结果 → RPC 结束。
- 异步客户端 + 异步服务器:客户端发起异步 RPC 请求 (非阻塞,立即返回) → 服务器 worker 线程收到请求 → 启动异步线程并立刻 return → 异步线程完成业务逻辑 → 调用 done->Run() 发送响应 → 客户端 IO/bthread (内部框架线程) 收到响应后,触发回调函数获取结果 → RPC 结束。
3.4 HTTP 客户端、HTTP 服务端
- 在搭建客户端的时候,特殊的是,需要告诉客户端我们要发送的是 HTTP 请求,而不是 RPC 请求。
- 在搭建服务端的时候,特殊的是,BRpc 支持通过 .proto 文件中定义服务,来支持对 JSON 格式 HTTP 请求正文的处理。相当于将 HTTP 请求当做 RPC 请求进行处理,将 JSON 请求解析后放入 requset 对象中,而不是 cntl 对象中。如果请求正文不是 JSON 格式,并且也没有通过 .proto 文件定义服务, 则将请求信息放在 cntl 对象中。
cpp
// http_client.cc
#include <brpc/channel.h>
#include "cal.pb.h"
int main()
{
// 1.实例化 ChannelOptions 信道选项对象
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_HTTP; // 设置为 HTTP 协议
// 2.实例化 Channel 信道对象
brpc::Channel channel;
channel.Init("192.168.174.128:9000", &options);
// 3.实例化 Controller 控制器对象,并设置 HTTP 请求信息
brpc::Controller cntl;
cntl.http_request().set_method(brpc::HTTP_METHOD_POST); // 设置为 POST 方法
cntl.http_request().uri().set_path("/CalService/Hello"); // 设置请求路径
cntl.http_request().SetHeader("Content-Type", "text/plain"); // 设置请求头
cntl.request_attachment().append("Hello World"); // 设置请求正文
// 4.发起 HTTP 请求
channel.CallMethod(nullptr, &cntl, nullptr, nullptr, nullptr);
// 5.打印 HTTP 响应正文
if (cntl.Failed()) {
std::cout << "HTTP 请求失败,原因:" << cntl.ErrorText() << std::endl;
return -1;
}
std::cout << cntl.response_attachment() << std::endl;
return 0;
}
cpp
// http_server.cc
#include <butil/logging.h>
#include <brpc/server.h>
#include "cal.pb.h"
// 创建 CalServiceImpl 类来实现 CalService 服务
class CalServiceImpl : public cal::CalService {
public:
CalServiceImpl() {}
~CalServiceImpl() {}
// 重写 Add 方法
virtual void Add(::google::protobuf::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddRsp* response,
::google::protobuf::Closure* done) override {
// 当 done_guard 被释放时执行 done->Run() 表明 RPC 同步请求完成
brpc::ClosureGuard done_guard(done);
int result = request->num1() + request->num2();
response->set_result(result);
}
// 重写 Hello 方法
virtual void Hello(::google::protobuf::RpcController* controller,
const ::cal::HelloReq* request,
::cal::HelloRsp* response,
::google::protobuf::Closure* done) override {
// 当 done_guard 被释放时执行 done->Run() 表明 RPC 同步请求完成
brpc::ClosureGuard done_guard(done);
brpc::Controller* cntl = static_cast<brpc::Controller*>(controller);
const brpc::HttpHeader& req = cntl->http_request(); // 获取 HTTP 请求
// 打印请求行
std::cout << "请求方法: " << brpc::HttpMethod2Str(req.method()) << std::endl;
std::cout << "请求URI: " << req.uri() << std::endl;
// 打印请求正文
std::cout << "请求正文: " << cntl->request_attachment() << std::endl;
// 设置响应正文、状态码
cntl->response_attachment().append("回显:" + cntl->request_attachment().to_string());
cntl->http_response().set_status_code(brpc::HTTP_STATUS_OK);
}
};
int main(int argc, char* argv[])
{
// 1.实例化计算服务对象
CalServiceImpl cal_service;
// 2.定义服务器配置对象
brpc::ServerOptions options;
options.idle_timeout_sec = -1; // 设置为 -1 表示不超时
// 3.实例化服务器对象
brpc::Server server;
// 4.向服务器添加计算服务
int ret = server.AddService(&cal_service, brpc::SERVER_DOESNT_OWN_SERVICE); // 服务对象不由服务器管理释放
if (ret == -1) {
std::cerr << "添加计算服务失败" << std::endl;
return -1;
}
// 5.启动服务器
ret = server.Start(9000, &options);
if (ret == -1) {
std::cerr << "启动服务器失败" << std::endl;
return -1;
}
// 6.等待服务器退出
server.RunUntilAskedToQuit();
return 0;
}
发送非 JSON 格式 HTTP 请求正文:

发送 JSON 格式 HTTP 请求正文:

六. BRpc 封装
1. 设计与实现
关键点:Rpc 通常与注册中心搭配使用的!
- 一个逻辑服务可以由多个主机节点(服务实例)共同提供,以实现负载均衡和高可用。
- 服务端在启动时,会向注册中心注册自己所提供的服务信息。
- 注册中心负责维护"服务 → 实例列表"的映射关系,并持续感知实例的上下线状态。
- 客户端在发起 RPC 调用前,先向注册中心查询可用的服务实例列表。
- 客户端从实例列表中选择一个节点(通常通过负载均衡策略,如随机、轮询、一致性哈希等),并向该节点发起 RPC 请求。
- 当服务实例发生变更(上线、下线、故障)时,注册中心会更新实例列表,客户端可动态感知或重新获取,从而实现透明的服务治理。
二次封装的目的:
- 封装 Channels 管理类:将所有能够提供指定服务的节点,提前创建好 Channel 给管理起来,以便于随时获取进行使用。
- Channels 集合类:一个服务有一个 Channels 集合 (因为一个服务,可能由很多个主机节点能够提供该服务),因此针对不同的服务,将它们各自的 Channels 集合管理起来,以 RR 轮转的负载均衡思想执行某个主机节点上的服务。
- ChannelsManage 管理类:管理的是服务对应的 Channels 集合,不关心的服务不需要管理。
- 封装 Closure 工厂类:主要针对异步请求时仿函数对象与 BRpc 不适配的情况。使用者提供一个函数/仿函数对象,我们返回一个 Closure 对象即可。
- 封装 Server 工厂类:主要针对 Server 对象的创建便捷化。
1.1 目录结构
bash
source/
|-- xzyrpc.cc
|-- xzyrpc.h
1.2 代码实现
cpp
// xzyrpc.h
#pragma once
#include <butil/logging.h>
#include <brpc/server.h>
#include <brpc/channel.h>
#include <mutex>
namespace xzyrpc {
using ChannelPtr = std::shared_ptr<brpc::Channel>;
// Channels 集合类:用于管理一个服务由多个主机节点提供的 channel
class Channels {
public:
Channels();
~Channels();
using ptr = std::shared_ptr<Channels>;
// 新增节点:根据 addr 新增一个 channel
void insert(const std::string& addr);
// 移除节点:根据 addr 删除一个 channel
void remove(const std::string& addr);
// 获取节点:轮询获取一个 channel 用于负载均衡提供服务
ChannelPtr select();
private:
std::mutex _mtx; // 互斥锁
uint32_t _idx; // 轮询下标
std::string _server_name; // 服务名称
std::vector<ChannelPtr> _channels; // 一个服务可以由多个主机节点提供
std::unordered_map<std::string, ChannelPtr> _map; // addr 映射 channel
};
// ChannelsManager 管理类:用于管理多个服务由多个主机节点提供的 channel
class ChannelsManager {
public:
ChannelsManager();
~ChannelsManager();
// 设置服务监控:根据 server_name 初始化一个 Channels 集合,添加到 _map 中进行管理
void setWatch(const std::string& server_name);
// 新增节点:根据 server_name 和 addr 新增一个 channel
void addNode(const std::string& server_name, const std::string& addr);
// 删除节点:根据 server_name 和 addr 删除一个 channel
void delNode(const std::string& server_name, const std::string& addr);
// 获取节点:根据 server_name 获取一个 channel
ChannelPtr getNode(const std::string& server_name);
private:
// 根据 server_name 获取 Channels 集合
Channels::ptr _Channels(const std::string& server_name);
private:
std::mutex _mtx; // 互斥锁
std::unordered_map<std::string, Channels::ptr> _map; // server_name 映射 Channels 集合
};
// ClosureFactory 工厂类:用于根据回调函数创建一个 Closure 类的实例
class ClosureFactory {
public:
using callback_t = std::function<void()>;
// 创建 Closure 类的实例:根据回调函数创建一个 Closure 类的实例
static google::protobuf::Closure* create(callback_t &&cb);
private:
struct Object {
using ptr = std::shared_ptr<Object>;
callback_t callback;
};
// 异步回调函数:用于在 Closure 类的实例完成时调用回调函数
static void asyncCallback(const Object::ptr obj);
};
// RpcServerFactory 工厂类:用于创建 RpcServer 类的实例
class RpcServerFactory {
public:
// 创建 RpcServer 类的实例:根据端口号和服务对象创建一个 RpcServer 类的实例
static std::shared_ptr<brpc::Server> create(int port, google::protobuf::Service* service);
};
}
cpp
// xzyrpc.cc
#include "xzyrpc.h"
#include "xzylog.h"
namespace xzyrpc {
Channels::Channels(): _idx(0) {}
Channels::~Channels() {}
using ptr = std::shared_ptr<Channels>;
// 新增节点:根据 addr 新增一个 channel
void Channels::insert(const std::string& addr) {
// 1.加锁:确保线程安全
std::unique_lock<std::mutex> lock(_mtx);
// 2.根据 addr 和 options 初始化一个 Channel 对象
ChannelPtr channel = std::make_shared<brpc::Channel>();
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_BAIDU_STD;
channel->Init(addr.c_str(), &options);
// 3.将 channel 插入到 _channels 中
_channels.push_back(channel);
// 4.将 addr 映射到 channel
_map.insert(std::make_pair(addr, channel));
}
// 移除节点:根据 addr 删除一个 channel
void Channels::remove(const std::string& addr) {
// 1.加锁:确保线程安全
std::unique_lock<std::mutex> lock(_mtx);
// 2.根据 addr 从 _map 中获取 channel
auto it = _map.find(addr);
if (it == _map.end()) {
WRN("删除节点时,节点 {} 不存在", addr);
return;
}
// 3.从 _map 中删除 addr 映射
_map.erase(it);
// 4.从 _channels 中删除 channel
for (auto vit = _channels.begin(); vit != _channels.end(); ++vit) {
if (*vit == it->second) {
vit = _channels.erase(vit);
break;
}
}
}
// 获取节点:轮询获取一个 channel 用于负载均衡提供服务
ChannelPtr Channels::select() {
// 1.加锁:确保线程安全
std::unique_lock<std::mutex> lock(_mtx);
// 2.轮询获取一个 channel
if (_channels.empty()) {
return nullptr;
}
ChannelPtr channel = _channels[_idx];
_idx = (_idx + 1) % _channels.size();
return channel;
}
ChannelsManager::ChannelsManager() {}
ChannelsManager::~ChannelsManager() {}
// 根据 server_name 获取 Channels 集合
Channels::ptr ChannelsManager::_Channels(const std::string& server_name) {
// 1.加锁:确保线程安全
std::unique_lock<std::mutex> lock(_mtx);
// 2.根据 server_name 从 _map 中获取 Channels 集合
auto it = _map.find(server_name);
if (it == _map.end()) {
return Channels::ptr();
}
return it->second;
}
// 设置服务监控:根据 server_name 初始化一个 Channels 集合,添加到 _map 中进行管理
void ChannelsManager::setWatch(const std::string& server_name) {
// 1.加锁:确保线程安全
std::unique_lock<std::mutex> lock(_mtx);
// 2.根据 server_name 初始化一个 Channels 集合
Channels::ptr channels = std::make_shared<Channels>();
// 3.将 channels 插入到 _map 中
_map.insert(std::make_pair(server_name, channels));
}
// 新增节点:根据 server_name 和 addr 新增一个 channel
void ChannelsManager::addNode(const std::string& server_name, const std::string& addr) {
// 1.根据 server_name 获取 Channels 集合
Channels::ptr channels = _Channels(server_name);
if (channels == nullptr) {
return;
}
// 2.根据 addr 新增一个 channel
channels->insert(addr);
}
// 删除节点:根据 server_name 和 addr 删除一个 channel
void ChannelsManager::delNode(const std::string& server_name, const std::string& addr) {
// 1.根据 server_name 获取 Channels 集合
Channels::ptr channels = _Channels(server_name);
if (channels == nullptr) {
return;
}
// 2.根据 addr 删除一个 channel
channels->remove(addr);
}
// 获取节点:根据 server_name 获取一个 channel
ChannelPtr ChannelsManager::getNode(const std::string& server_name) {
// 1.根据 server_name 获取 Channels 集合
Channels::ptr channels = _Channels(server_name);
if (channels == nullptr) {
return ChannelPtr();
}
// 2.根据轮询策略获取一个 channel
return channels->select();
}
// 创建 Closure 类的实例:根据回调函数创建一个 Closure 类的实例
google::protobuf::Closure* ClosureFactory::create(callback_t &&cb) {
Object::ptr obj = std::make_shared<Object>();
obj->callback = std::move(cb);
return brpc::NewCallback(&ClosureFactory::asyncCallback, obj);
}
// 异步回调函数:用于在 Closure 类的实例完成时调用回调函数
void ClosureFactory::asyncCallback(const Object::ptr obj) {
obj->callback();
}
// 创建 RpcServer 类的实例:根据端口号和服务对象创建一个 RpcServer 类的实例
std::shared_ptr<brpc::Server> RpcServerFactory::create(int port, google::protobuf::Service* service) {
// 1.初始化 ServerOptions
brpc::ServerOptions options;
options.idle_timeout_sec = -1; // 禁用空闲超时
// 2.创建 Server 实例
std::shared_ptr<brpc::Server> server = std::make_shared<brpc::Server>();
int ret = server->AddService(service, brpc::SERVER_OWNS_SERVICE); // 由服务端负责释放 service
if (ret == -1) {
ERR("添加服务失败!");
abort(); // 终止程序
}
ret = server->Start(port, &options);
if (ret == -1) {
ERR("启动服务失败!");
abort();
}
return server;
}
}
2. 使用样例
2.1 目录结构
bash
test/
|-- brpc
|-- cal.pb.cc
|-- cal.pb.h
|-- cal.proto
|-- makefile
|-- rpc_client.cc
|-- rpc_server.cc
2.2 项目构建
bash
# makefile
all: rpc_server rpc_client
rpc_server: rpc_server.cc cal.pb.cc ../../source/xzyrpc.cc ../../source/xzylog.cc
g++ -o $@ $^ -std=c++17 -lbrpc -lleveldb -lprotobuf -lpthread -ldl -lssl -lcrypto -lgflags -lfmt -lspdlog
rpc_client: rpc_client.cc cal.pb.cc ../../source/xzyrpc.cc ../../source/xzylog.cc
g++ -o $@ $^ -std=c++17 -lbrpc -lleveldb -lprotobuf -lpthread -ldl -lssl -lcrypto -lgflags -lfmt -lspdlog
%.pb.cc: %.proto
protoc --cpp_out=./ $^
.PHONY: clean
clean:
rm -f rpc_server rpc_client cal.pb.cc
2.3 代码实现
cpp
// rpc_client.cc
#include "../../source/xzyrpc.h"
#include "../../source/xzylog.h"
#include "cal.pb.h"
int main(int argc, char* argv[])
{
// 1.创建 ChannelsManager 实例
xzyrpc::ChannelsManager channels_manager;
// 2.关注 calculate 服务
channels_manager.setWatch("calculate");
// 3.添加 calculate 服务节点
channels_manager.addNode("calculate", "192.168.174.128:9000");
// 4.获取 calculate 服务节点
xzyrpc::ChannelPtr channel = channels_manager.getNode("calculate");
if (channel == nullptr) {
ERR("没有可供 calculate 服务节点可供使用");
abort();
}
// 5.创建 Controller、AddReq、AddRsp、Closure 实例
brpc::Controller* cntl = new brpc::Controller();
cal::AddReq* req = new cal::AddReq();
req->set_num1(10);
req->set_num2(20);
cal::AddRsp* rsp = new cal::AddRsp();
google::protobuf::Closure* closure = xzyrpc::ClosureFactory::create([=](){
std::unique_ptr<brpc::Controller> cntl_guard(cntl);
std::unique_ptr<cal::AddReq> req_guard(req);
std::unique_ptr<cal::AddRsp> rsp_guard(rsp);
if (cntl_guard->Failed() == true) {
ERR("RPC 调用失败: %s", cntl_guard->ErrorText());
return;
}
std::cout << "RPC 调用结果:" << rsp_guard->result() << std::endl;
});
// 6.创建 CalService_Stub 实例
cal::CalService_Stub stub(channel.get());
// 7.发起 RPC 调用
stub.Add(cntl, req, rsp, closure);
std::cout << "====================" << std::endl;
getchar();
return 0;
}
cpp
// rpc_server.cc
#include "../../source/xzyrpc.h"
#include "cal.pb.h"
// 创建 CalServiceImpl 类来实现 CalService 服务
class CalServiceImpl : public cal::CalService {
public:
CalServiceImpl() {}
~CalServiceImpl() {}
// 重写 Add 方法
virtual void Add(::google::protobuf::RpcController* controller,
const ::cal::AddReq* request,
::cal::AddRsp* response,
::google::protobuf::Closure* done) override {
// 当 done_guard 被释放时执行 done->Run() 表明 RPC 同步请求完成
brpc::ClosureGuard done_guard(done);
int result = request->num1() + request->num2();
response->set_result(result);
}
};
int main(int argc, char* argv[])
{
// 1.创建 CalServiceImpl 实例
CalServiceImpl* cal_server = new CalServiceImpl();
// 2.创建 RpcServer 实例
std::shared_ptr<brpc::Server> server = xzyrpc::RpcServerFactory::create(9000, cal_server);
// 3.启动 RPC 服务器
server->RunUntilAskedToQuit();
return 0;
}
