项目技术点
- 集群和分布式概念以及原理
- RPC远程过程调用原理以及实现
- Protobuf数据序列化和反序列化协议
- ZooKeeper分布式一致性协调服务应用以及编程
- muduo网络库编程
- conf配置文件读取
- 异步日志(单线程异步互斥锁队列)
- 源码:https://github.com/paradise2017/Distributed-RPC-Protobuf-Communication-Framework
集群和分布式
- 集群:一台服务器独立运行一个工程的所有模块
- 分布式:单个工程拆分很多模块,每一个模块独立部署在一个服务器主机上,所有服务器协同工作共同提供服务; 单台服务器称为节点,根据节点的并发需求,对节点可以再做节点模块集群部署。
- 集群和单机特点
- 分布式特点(按需分配资源)
分布式问题解决
- 不同机器间,进程间的通信以及访问?rpc通信
项目整体框架
Protobuf
- 下载安装
bash
参考:https://github.com/protocolbuffers/protobuf#readme
https://github.com/protocolbuffers/protobuf/blob/v3.20.3/src/README.md
1:安装流程
(clone)
git clone https://github.com/protocolbuffers/protobuf.git
# 可以使用https://ghproxy.com/作为代理,会快很多,如下:
git clone https://ghproxy.com/https://github.com/protocolbuffers/protobuf.git
(install)
1:unzip protobuf-master.zip // 解压
2:cd protobuf-master // 进入目录
3:sudo apt-get install autoconf automake libtool curl make g++ unzip // 安装依赖
4:./autogen.sh // 生成配置文件
5:./configure // 配置环境
6:make // 编译
7:sudo make install // 安装
8:sudo ldconfig // 刷新动态库
- config
test.proto
cpp
syntax = "proto3"; // protobuf版本
package proto; // 代码所在的包,(C++ namespace)
option cc_generic_services = true;// 定义选项,生成service服务类和rpc方法
message ResultCode{
int32 errcode = 1;
bytes errmsg = 2;
}
// 1:定义登录消息类型
message LoginRequest{ // protobuf生成C++LoginRequest类
bytes name = 1; // 第一个字段 // 对于字符串建议改成 bytes
bytes pwd = 2; // 第二个字段
// name pwd LoginRequest 成员变量
// protobuf 提供成员变量的crud
}
// 2:定义登录响应消息类型(对象类型)
message LoginResponse{
ResultCode result = 1;
bool success = 2;
}
message GetFriendListsRequest{
uint32 userid = 1;
}
// 3:枚举类型
message User{
bytes name = 1;
uint32 age = 2;
enum Sex{
MAN = 0;
WOMAN = 1;
}
Sex sex = 3;
}
// 4:对象&列表类型
message GetFriendListsResponse{
ResultCode result = 1;
repeated User friend_list = 2; // 定义列表类型
}
// protobuf定义rpc方法 -service
service UserServiceRpc{
rpc Login(LoginRequest) returns(LoginResponse);
rpc GetFriendLists(GetFriendListsRequest) returns(GetFriendListsResponse);
}
// rpc
// 客户端 caller :UserServiceRpc_Stub Login, GetFriendLists, RpcChannel(抽象类)重写
// 服务端(执行rpc) callee:UserServiceRpc Login, GetFriendLists, GetDescriptor
// 命令执行 protoc test.proto --cpp_out=./
// 生成 test.pb.cc test.pb.h
cpp
#include "test.pb.h"
#include <iostream>
#include <string>
using namespace proto;
int main()
{
// 打包对象及数据
LoginRequest req;
req.set_name("zhang san");
req.set_pwd("123456");
// 数据序列号 ->char*
std::string send_str;
if (req.SerializeToString(&send_str))
{
std::cout << send_str << std::endl; // zhang san123456 send_str.size()=19
std::cout << send_str.size() << std::endl; // zhang san123456 send_str.size()=19
}
// 反序列化
// 字符流解析对象
LoginRequest req_b;
if (req_b.ParseFromString(send_str))
{
std::cout << req.name() << std::endl; // zhang san
std::cout << req.pwd() << std::endl; // 123456
}
LoginResponse rsp;
ResultCode *rc = rsp.mutable_result(); // 成员对象的使用
rc->set_errcode(0); // 设置成员对象的值
rc->set_errmsg("login failed");
GetFriendListsResponse rsp;
ResultCode *rc = rsp.mutable_result();
rc->set_errcode(0);
// 处理列表类型,返回创建对象指针
User *user_1 = rsp.add_friend_list(); // 成员列表的使用1:生成对象并返回地址
user_1->set_name("zhang san");
user_1->set_age(20);
user_1->set_sex(User::MAN);
User *user_2 = rsp.add_friend_list();
user_2->set_name("zhang ming");
user_2->set_age(22);
user_2->set_sex(User::WOMAN);
std::cout << rsp.friend_list_size() << std::endl; // User个数
return 0;
}
// 编译生成
// g++ main.cc test.pb.cc -lprotobuf
Zookeeper
bash
zookeeper
sudo apt update
sudo apt install openjdk-11-jdk
java -version
openjdk version "11.0.x" 2024-07-xx
OpenJDK Runtime Environment (build 11.0.x+xx-Ubuntu-x)
OpenJDK 64-Bit Server VM (build 11.0.x+xx-Ubuntu-x, mixed mode, sharing)
zookeeper
客户端,添加watcher 事件回调
缺点
1:只可以存储字节byte数组
2:存储对象,无法发送
3:
ls /
get /节点信息
4:主要作用:
1:服务配置中心,记录所有rpc主机,特殊的文件系统
2:分布式锁
3:
- 编译问题
Linux安装zookeeper原生C API接口出现的make编译错误_error: '%d' directive writing between 1 and 5 byte-CSDN博客
zookeeper中间件流程图。
项目中的建议
- 二进制存储和文本存储
二进制存储占用字节数少,网络传输节省带宽资源。
例如存储 1000 整数
字符流 "100" 3个字节
字节流 01100100 1个字节
1:二进制格式 vs. 文本格式
Protobuf 使用的是二进制格式进行数据序列化,而 JSON 使用的是文本格式。二进制格式通常比文本格式更紧凑,因为二进制数据的表示比文本格式所需的字节数要少。例如,数字和布尔值等数据类型在 Protobuf 中使用定长或可变长的二进制表示,而 JSON 则会将这些数据转换为字符串,这通常会消耗更多的空间。
例如,数字 42 在 JSON 中被表示为字符串 "42",而在 Protobuf 中,它是一个定长的整数,通常只占 4 个字节。
2:字段标识符
在 Protobuf 中,每个字段都有一个唯一的数字标识符,而不是使用 JSON 中常见的 字符串键(如 "name": "John")。这使得 Protobuf 可以节省大量的空间,因为数字标识符比字符串键要短得多。例如,字段名 "name" 可能需要 4 个字节,而字段编号(如 1)只需要一个字节。
3:数据类型表示方式
Protobuf 明确定义了数据类型(如整数、字符串、浮点数等),并且使用了高效的编码方式(比如 varint 编码),因此在编码时对每种数据类型使用了最小的存储空间。而 JSON 只是一个文本表示,没有内建的高效编码方式。例如,整数在 JSON 中总是以字符串形式传输,这比直接使用二进制编码要大得多。
4:string
在 C++ 中,std::string 是一个用于存储文本的容器类,默认情况下,它是用来存储字符(通常是 char 类型)序列的。然而,std::string 本质上是一个字节数组,它的每个元素都是一个 char 类型的字节。
demo
#include <iostream>
#include <string>
int main() {
// 创建一个包含字节数据的 std::string
std::string recv_buf = {0x41, 0x42, 0x43}; // 对应字符 'A', 'B', 'C'
// 输出存储的字节数据
for (unsigned char byte : recv_buf) {
std::cout << "Byte: " << std::hex << (int)byte << std::endl;
}
return 0;
}
out:
Byte: 41
Byte: 42
Byte: 43
- CMakeLists.txt
bash
aux_source_directory(. SRC_LIST)
项目新添加.cc
. 这种写法需要重新编译
cd build
rm -rf *
cmake..
make
- GDB调试
bash
1:CMakeLists.txt 配置
# 生成dubug版本,可以GDB调试
set(CMAKE_BUILD_TYPE "Debug")
2:启动
gdb ./provider
3:打断点 (可调试so库)
break mprpc_config.cc:17
4:运行(test.conf 运行文件需要的配置argc,argv)
run -i test.conf
5:查看堆栈
bt
6:查看代码
l
7:单步执行
next
8:打印
p src_buf
9: 退出
q
- linux库
bash
sudo find /usr -name "libmuduo*"
ps -ef | grep zookeeper.out
ps:表示"process status"(进程状态),用于显示当前系统的进程信息。
-e 或 -A:显示所有进程(包括其他用户的进程),通常不加 -e 参数时只显示当前用户的进程。
-f:显示完整的进程信息,提供更多详细的列。
tips : 安装动态库之后缓冲更新
sudo ldconfig