【C++项目】从零实现RPC框架「一」:项目准备与前置知识学习

发布于:2025-03-08 ⋅ 阅读:(149) ⋅ 点赞:(0)

🌈 个人主页:Zfox_
🔥 系列专栏:C++从入门到精通

一:🚀 项⽬介绍

🧑‍💻 RPC(RemoteProcedureCall) 远程过程调⽤,是⼀种通过⽹络从远程计算机上请求服务,⽽不需要了解底层⽹络通信细节RPC 可以使⽤多种⽹络协议进⾏通信,如 HTTPTCPUDP 等,并且在 TCP/IP ⽹络四层模型 中跨越了传输层和应⽤层。简⾔之 RPC 就是像调⽤本地⽅法⼀样调⽤远程⽅法

过程可以理解为业务处理、计算任务,更直⽩的说,就是程序/⽅法/函数等,就是像调⽤本地⽅法⼀样调⽤远程⽅法。

🌰 举个形象的例⼦:谈恋爱例⼦

🌹 本地过程调⽤:恋爱对象在你的⾝边,可以随时约对象吃饭、看电影、约会等等

💗 远端过程调⽤:好像异地恋⼀样,隔着千⼭万⽔,如果想约会,需要先和对象进⾏约定,在坐⽕⻋ / ⻜机赶到约定的地点

⼀个完整 RPC 通信框架,⼤概包含以下内容:

  • 序列化协议
  • 通信协议
  • 连接复⽤
  • 服务注册
  • 服务发现
  • 服务订阅和通知
  • 负载均衡
  • 服务监控
  • 同步调⽤
  • 异步调⽤

💻 我们的项⽬是基于 C++JsonCppmuduo ⽹络库 实现⼀个简单、易⽤的 RPC 通信框架,即使是不懂⽹络的开发者也可以很快速的上⼿,它实现了同步调⽤、异步callback调⽤、异步futrue调⽤、服务注册 / 发现,服务上线 / 下线 以及 发布订阅 等功能设计。

二:🔥 技术选型

  1. ⽬前 RPC 的实现⽅案有两种:
  • clientserver 继承 公共接⼝
    • 根据 IDL(接⼝描述语⾔)定义公共接⼝
    • 编写代码⽣成器根据IDL语⾔⽣成相关的C++、Java代码
    • 然后我们的客⼾端和服务器程序共同向上继承公共接⼝即可
    • ⽐如我们常⽤的 Protobufjson 可以定义IDL接⼝,并⽣成 RPC 相关的代码
    • 缺点:使⽤ Protobuf 因为⽣成⼀部分代码,所以对理解不够友好;如果是 json 定义 IDL 语⾔需要⾃⼰编写代码⽣成器难度较⼤⼀点,暂不考虑这种⽅案
    • 实现⼀个远程调⽤接⼝call,然后通过传⼊函数名参数来调⽤ RPC 接⼝,我们采⽤这种实现⽅案
  1. ⽹络传输的参数和返回值怎么映射到对应的 RPC接⼝上?
  • 使⽤ protobuf 的反射机制
  • 使⽤ C++模板、类型萃取、函数萃取 等机制
  • 使⽤更通⽤的类型,⽐如JSON类型,设计好参数和返回值协议即可
  • 前两种技术难度和学习成本较⾼,我们使⽤第三种⽅式
  1. ⽹络传输怎么做?
  • 原⽣ socket-实现难度较⼤,暂不考虑
  • Boostasio 库的异步通信-需要扩展 boost
  • muduo库-学习开发成本较低
  1. 序列化和反序列化?
  • Protobuf :可选
  • JSON:因为项⽬需要使⽤JSON来定义函数参数和返回值,所以砸⻔项⽬中直接采⽤ JSON 进⾏序
    列化和反序列化

三:🔥 开发环境

  • Linux(Ubuntu-20.04)
  • VSCode/Vim
  • g++ / gdb
  • Makefile

四:🔥 Ubuntu-22.04环境搭建

⬇️ 安装 wget (⼀般情况下默认会⾃带)

$ sudo apt-get install wget

🪞 更换国内软件源
先备份原来的 /etc/apt/source.list ⽂件

$ sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

🆕 添加软件源⽂件内容,新增以下内容

deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
#添加清华源
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse

新增完毕后,更新源

$ sudo apt-get update

⬇️ 安装 lrzsz 传输⼯具

$ sudo apt-get install lrzsz

⬇️ 安装编译器 gcc/g++

$ sudo apt-get install gcc g++

⬇️ 安装项⽬构建⼯具 make

$ sudo apt-get install make

⬇️ 安装调试器 gdb

$ sudo apt-get install gdb

⬇️ 安装 git

$ sudo apt-get install git

⬇️ 安装 cmake

sudo apt-get install cmake

⬇️ 安装 jsoncpp

$ sudo apt-get install libjsoncpp-dev

⬇️ 安装 Muduo

  • 下载源码
# git⽅式
$ git clone https://github.com/chenshuo/muduo.git

👝 下载不了的也可以使用博主的 gitee下载 zip文件

git clone https://gitee.com/zfox-f/rpc-framework-implementation.git

然后把 build 删掉,这里是已经安装出来的信息,然后进入 third 解压和运⾏脚本编译安装

unzip muduo-master.zip
./build.sh
./build.sh install
  • ⬇️ 安装依赖环境
$ sudo apt-get install libz-dev libboost-all-dev

💻 注意:这里有些人可能会出现这个问题
在这里插入图片描述
原因是 Linux版本为(Ubuntu-22.04)以上, 需要切换回 20.04

这个问题就是应该你当前的系统中内置了 python3.10,而 boost 库 下载需要使用到 python3.9,所以出现这个问题,由于是内置的 python 版本,所以无法删除,导致这个问题目前只能是切换操作系统为 ubuntu20.04 才能够解决

☁️ 运⾏脚本编译安装

$ unzip muduo-master.zip
$ ./build.sh
$ ./build.sh install

五:🔥 第三⽅库使⽤介绍

🦋 JsonCpp库

🎀 Json数据格式

Json 是⼀种数据交换格式,它采⽤完全独⽴于编程语⾔的⽂本格式来存储和表⽰数据。

例如:我们想表⽰⼀个同学的学⽣信息

  • C 代码表⽰
char *name = "xx";
int age = 18;
float score[3] = {
   88.5, 99, 58};
  • Json 表⽰
{
   
	"姓名" : "xx",
	"年龄" : 18,
	"成绩" : [88.5, 99, 58],
	"爱好"{
   
		"书籍" : "西游记",
		"运动" : "打篮球"
	}
}

Json 的数据类型包括对象,数组,字符串,数字等。

  • 对象使⽤花括号 {} 括起来的表⽰⼀个对象
  • 数组使⽤中括号 [] 括起来的表⽰⼀个数组
  • 字符串使⽤常规双引号 “” 括起来的表⽰⼀个字符串
  • 数字包括整形和浮点型,直接使⽤

🎀 JsonCpp介绍

🔬 Jsoncpp 库主要是⽤于实现 Json 格式数据的序列化和反序列化,它实现了将多个数据对象组织成为 json 格式字符串,以及将 Json 格式字符串解析得到多个数据对象的功能。

👀 先看⼀下 Json 数据对象类的表⽰

class Json::Value
{
   
	Value &operator=(const Value &other); 		//Value重载了[]和=,因此所有的赋值和获取数据都可以通过
	Value& operator[](const std::string& key);	//简单的⽅式完成 val["name"] ="xx";
	Value& operator[](const char* key);
	Value removeMember(const char* key);	//移除元素
	const Value& operator[](ArrayIndex index) const; //val["score"][0]
	Value& append(const Value& value);		//添加数组元素val["score"].append(88);
	ArrayIndex size() const;			//获取数组元素个数 val["score"].size();
	std::string asString() const;		//转string string name =
	val["name"].asString();
	const char* asCString() const;		//转char* char *name =
	val["name"].asCString();
	Int asInt() const;					//转int int age = val["age"].asInt();
	float asFloat() const;				//转float float weight = val["weight"].asFloat();
	bool asBool() const;				//转 bool bool ok = val["ok"].asBool();
};

Jsoncpp 库主要借助三个类以及其对应的少量成员函数完成序列化及反序列化

  • 序列化接⼝
class JSON_API StreamWriter {
   
		virtual int write(Value const& root, std::ostream* sout) = 0;
}

class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
   
	virtual StreamWriter* newStreamWriter() const;
}
  • 反序列化接⼝
class JSON_API CharReader {
   
virtual bool parse(char const* beginDoc, char const* endDoc,
	Value* root, std::string* errs) = 0;
}

class JSON_API CharReaderBuilder : public CharReader::Factory {
   
	virtual CharReader* newCharReader() const;
}

🎀 JsonCpp使⽤

jsoncpp.cpp

#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include <jsoncpp/json/json.h>

// 实现字符串的序列化
bool Serialize(const Json::Value &val, std::string &body)
{
   
    std::stringstream ss;

    // 先实例化一个工厂类对象
    Json::StreamWriterBuilder swb;
    // 再使用工厂类对象来生产派生类
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter