[C++项目] Mprpc分布式网络通信框架

项目技术点

  1. 集群和分布式概念以及原理
  2. RPC远程过程调用原理以及实现
  3. Protobuf数据序列化和反序列化协议
  4. ZooKeeper分布式一致性协调服务应用以及编程
  5. muduo网络库编程
  6. conf配置文件读取
  7. 异步日志(单线程异步互斥锁队列)
  8. 源码:https://github.com/paradise2017/Distributed-RPC-Protobuf-Communication-Framework

集群和分布式

  • 集群:一台服务器独立运行一个工程的所有模块
  • 分布式:单个工程拆分很多模块,每一个模块独立部署在一个服务器主机上,所有服务器协同工作共同提供服务; 单台服务器称为节点,根据节点的并发需求,对节点可以再做节点模块集群部署。
  • 集群和单机特点
  • 分布式特点(按需分配资源)

分布式问题解决

  1. 不同机器间,进程间的通信以及访问?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                                               

main.cc

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
相关推荐
java1234_小锋36 分钟前
RabbitMQ如何构建集群?
分布式·rabbitmq
zmd-zk5 小时前
spark将数据输出到hive或mysql中
大数据·数据库·hive·分布式·python·mysql·spark
东方佑5 小时前
spark 分布式 原理
大数据·分布式·spark
明达技术6 小时前
MR30分布式IO模块:驱动物流传输机高效升级
分布式·物联网·自动化·制造
喜欢猪猪7 小时前
秒杀抢购场景下实战JVM级别锁与分布式锁
jvm·分布式
LeonNo119 小时前
k8s,理解容器中namespace和cgroups的原理
分布式·容器·kubernetes
Java 第一深情9 小时前
分布式全文检索引擎ElasticSearch-数据的写入存储底层原理
分布式·elasticsearch·全文检索
明达技术10 小时前
MR30分布式IO模块,为港口岸桥安全增效保驾护航
分布式·安全