项目技术点
- 集群和分布式概念以及原理
- RPC远程过程调用原理以及实现
- Protobuf数据序列化和反序列化协议
- ZooKeeper分布式一致性协调服务应用以及编程
- muduo网络库编程
- conf配置文件读取
- 异步日志(单线程异步互斥锁队列)
- 源码:https://github.com/paradise2017/Distributed-RPC-Protobuf-Communication-Framework
集群和分布式
- 集群:一台服务器独立运行一个工程的所有模块
- 分布式:单个工程拆分很多模块,每一个模块独立部署在一个服务器主机上,所有服务器协同工作共同提供服务; 单台服务器称为节点,根据节点的并发需求,对节点可以再做节点模块集群部署。
- 集群和单机特点
- 分布式特点(按需分配资源)
分布式问题解决
- 不同机器间,进程间的通信以及访问?rpc通信
项目整体框架
Protobuf
- 下载安装
参考: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
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; // 第一个字段 // 对于字符串建议改成 bytesbytes 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
main.cc
#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()=19std::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 sanstd::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
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::stringstd::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
aux_source_directory(. SRC_LIST)项目新添加.cc
. 这种写法需要重新编译cd build
rm -rf *
cmake..
make
- GDB调试
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.conf5:查看堆栈
bt
6:查看代码
l
7:单步执行
next
8:打印
p src_buf
9: 退出
q
- linux库
sudo find /usr -name "libmuduo*"ps -ef | grep zookeeper.out
ps:表示"process status"(进程状态),用于显示当前系统的进程信息。
-e 或 -A:显示所有进程(包括其他用户的进程),通常不加 -e 参数时只显示当前用户的进程。
-f:显示完整的进程信息,提供更多详细的列。tips : 安装动态库之后缓冲更新
sudo ldconfig