RPC框架源码分析学习(二)

news2025/5/18 21:29:45

RPC框架源码分析与原理解读

前言

在分布式系统开发中,远程过程调用(RPC)是一项基础且关键的技术。通过对KVstorageBaseRaft-cpp项目RPC模块的源码分析,我深入理解了RPC框架的工作原理和实现细节。本文将从程序员视角分享我的学习心得。

框架概述

本项目中的RPC框架是一个基于Google Protobuf和Muduo网络库实现的C++版RPC框架。它支持服务注册、服务发现和远程方法调用,提供了简洁的接口和高效的数据传输机制。

核心组件分析

1. RPC通信协议

首先,我分析了RPC通信协议的定义文件rpcheader.proto

syntax = "proto3";
package RPC;

// RPC请求和响应的消息格式
message RpcHeader {
    string service_name = 1;  // 服务名
    string method_name = 2;   // 方法名
    uint32 args_size = 3;     // 参数大小
}

这个定义非常精简,包含了服务名、方法名和参数大小三个关键信息,这些信息构成了RPC请求的头部。

2. RPC客户端实现

2.1 MprpcChannel类

MprpcChannel是客户端发起RPC调用的核心类,继承自google::protobuf::RpcChannel

class MprpcChannel : public google::protobuf::RpcChannel {
public:
    // 构造函数,支持立即连接或延迟连接
    MprpcChannel(string ip, short port, bool connectNow);
    // 关键方法:负责序列化请求、发送请求并接收响应
    void CallMethod(const google::protobuf::MethodDescriptor* method,
                   google::protobuf::RpcController* controller,
                   const google::protobuf::Message* request,
                   google::protobuf::Message* response,
                   google::protobuf::Closure* done) override;
    // 其他辅助方法...
};

CallMethod的实现最为关键,它完成了:

  1. 获取服务名和方法名
  2. 序列化请求参数
  3. 构造RPC请求头
  4. 发送请求并等待响应
  5. 解析响应数据

我特别注意到请求消息格式的设计:

header_size(4字节变长编码) + header_str + args_str

这种设计保证了网络传输的高效性和兼容性。

3. RPC服务端实现

3.1 RpcProvider类

RpcProvider是服务端的核心类,负责服务注册和请求处理:

class RpcProvider {
public:
    // 注册服务
    void NotifyService(google::protobuf::Service *service);
    // 启动RPC服务
    void Run(int nodeIndex, short port);
private:
    // 连接回调
    void OnConnection(const muduo::net::TcpConnectionPtr &);
    // 消息回调,处理RPC请求
    void OnMessage(const muduo::net::TcpConnectionPtr &, muduo::net::Buffer *, muduo::Timestamp);
    // 发送RPC响应
    void SendRpcResponse(const muduo::net::TcpConnectionPtr &, google::protobuf::Message *);
    // 其他成员...
};
3.2 服务注册机制

服务注册利用了Protobuf的反射机制,通过NotifyService方法实现:

void RpcProvider::NotifyService(google::protobuf::Service *service) {
    ServiceInfo service_info;
    const google::protobuf::ServiceDescriptor *pserviceDesc = service->GetDescriptor();
    std::string service_name = pserviceDesc->name();
    int methodCnt = pserviceDesc->method_count();
    
    for (int i = 0; i < methodCnt; ++i) {
        const google::protobuf::MethodDescriptor *pmethodDesc = pserviceDesc->method(i);
        std::string method_name = pmethodDesc->name();     
        service_info.m_methodMap.insert({method_name, pmethodDesc});
    }
    service_info.m_service = service;
    m_serviceMap.insert({service_name, service_info});
}

这段代码通过Protobuf的反射机制获取服务描述符和方法描述符,并将它们存储在哈希表中,以便后续查找和调用。

3.3 请求处理流程

OnMessage方法处理接收到的RPC请求:

  1. 解析请求头,获取服务名、方法名和参数大小
  2. 查找对应的服务和方法
  3. 反序列化请求参数
  4. 创建响应对象和回调
  5. 调用目标方法

最关键的部分是动态调用目标方法:

service->CallMethod(method, nullptr, request, response, done);

这里用到了Protobuf的动态调用机制,method是之前通过反射获取的方法描述符,done是一个回调对象,用于处理方法执行完成后的操作。

3.4 回调机制实现

回调函数的创建使用了Protobuf提供的NewCallback模板函数:

google::protobuf::Closure *done =
    google::protobuf::NewCallback<RpcProvider, 
                                 const muduo::net::TcpConnectionPtr &, 
                                 google::protobuf::Message *>(
        this, &RpcProvider::SendRpcResponse, conn, response);

这段代码创建了一个绑定了当前对象、连接和响应对象的回调函数,在RPC方法执行完成后将被调用,用于发送响应数据。

实例分析:RPC示例代码

通过分析example/rpcExample目录下的示例,进一步理解了RPC框架的使用方法。

1. 服务定义

service FiendServiceRpc {
    rpc GetFriendsList(GetFriendsListRequest) returns (GetFriendsListResponse);
}

2. 服务实现

class FriendService : public fixbug::FiendServiceRpc {
public:
    void GetFriendsList(google::protobuf::RpcController* controller,
                      const fixbug::GetFriendsListRequest* request,
                      fixbug::GetFriendsListResponse* response,
                      google::protobuf::Closure* done) override {
        // 业务逻辑实现
        response->mutable_result()->set_errcode(0);
        response->mutable_result()->set_errmsg("");
        done->Run();  // 调用完成,发送响应
    }
};

3. 服务注册与启动

int main(int argc, char** argv) {
    // 创建RPC服务提供者
    RpcProvider provider;
    // 创建服务对象
    FriendService friendService;
    // 注册服务
    provider.NotifyService(&friendService);
    // 启动RPC服务,指定节点ID和端口
    provider.Run(1, 8000);
    return 0;
}

技术难点解析

1. 模板与回调函数

RPC框架中大量使用了C++模板和回调函数,这是一个重要的技术点。特别是NewCallback函数的使用:

google::protobuf::NewCallback<Class, ArgType1, ArgType2>(
    this, &Class::Method, arg1, arg2);

这里模板参数<Class, ArgType1, ArgType2>指定了回调函数的类型和参数类型,而函数参数则提供了具体的对象、方法和参数值。

2. 序列化与网络传输

RPC框架的另一个关键点是如何高效地序列化和网络传输。我分析了其实现方式:

  1. 使用Protobuf进行序列化,保证了跨平台兼容性
  2. 采用"头部大小+头部内容+参数内容"的消息格式,解决了TCP流数据的边界问题
  3. 使用Muduo网络库处理TCP连接和事件回调,提供了高性能的网络IO

2025.5.15

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2377300.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【测试】BUG

目录 1、描述BUG的要素&#xff1a; 2、BUG的级别 3、BUG的状态的流转 4、与开发产⽣争执怎么办&#xff08;⾼频考题&#xff09; 什么是BUG&#xff1f;&#xff1f;&#xff1f; 程序与规格说明之间的不匹配才是错误 1、描述BUG的要素&#xff1a; 问题出现的版本、问…

Mac 环境下 JDK 版本切换全指南

概要 在 macOS 上安装了多个 JDK 后&#xff0c;可以通过系统自带的 /usr/libexec/java_home 工具来查询并切换不同版本的 Java。只需在终端中执行 /usr/libexec/java_home -V 列出所有已安装的 JDK&#xff0c;然后将你想使用的版本路径赋值给环境变量 JAVA_HOME&#xff0c;…

Pillow 移除或更改了 FreeTypeFont.getsize() 方法

w, h self.font.getsize(label) # text width, height AttributeError: FreeTypeFont object has no attribute getsize 在Pillow 项目的变更日志里可以查到哪个版本移除了 getsize() 方法&#xff0c;Pillow仓库&#xff1a; Releases python-pillow/Pillow GitHub 因为…

视频编辑软件无限音频、视频、图文轨

威力导演APP的特色功能包括无限音频、视频、图文轨&#xff0c;以及上百种二/三维特技转场、音/视频滤镜和多种音视频混编输出。此外&#xff0c;它还支持实时高清HDV格式、模拟信号输出&#xff0c;并具有DV25、DVACM、DV、HDV输入和输出等功能。在视频编辑领域&#xff0c;威…

uniapp-商城-53-后台 商家信息(更新修改和深浅copy)

1、概述 文章主要讨论了在数据库管理中如何处理用户上传和修改商家信息的问题&#xff0c;特别是通过深浅拷贝技术来确保数据更新的准确性和安全性。 首先&#xff0c;解释了深拷贝和浅拷贝的区别&#xff1a;浅拷贝使得两个变量共享相同的内存地址&#xff0c;而深拷贝则创建新…

[Java实战]Spring Boot 整合 Thymeleaf (十)

[Java实战]Spring Boot 整合 Thymeleaf &#xff08;十&#xff09; 引言 在 Java Web 开发领域&#xff0c;Thymeleaf 以其自然模板、无缝 Spring 集成和强大的表达式引擎脱颖而出&#xff0c;成为 Spring Boot 官方推荐的模板引擎。本文将深度解析 Spring Boot 与 Thymelea…

监控易一体化运维:网络流量分析的智慧引擎

在数字化时代&#xff0c;企业运营与网络紧密相连&#xff0c;网络性能的优劣直接影响企业的发展步伐。网络流量管理在企业网络运维中占据非常关键的地位。监控易一体化运维管理软件&#xff0c;凭借其强大的网络流量分析功能&#xff0c;为企业网络的稳定高效运行提供了有力保…

IDEA+git将分支合并到主分支、IDEA合并分支

文章目录 一、合并分支二、可能遇到的问题2.1、代码冲突 开发过程中我们可能在开发分支(dev)中进行开发&#xff0c;等上线后将代码合并到主分支(master)中&#xff0c;本文讲解如何在IDEA中将dev分支的代码合并到master分支中。 一、合并分支 功能说明&#xff1a;将dev分支的…

uniapp+vue3中自动导入ref等依赖

前言&#xff1a; 在我们使用uni-appvue3创建项目&#xff0c;开发的过程中&#xff0c;老是需要导入我们的ref、onshow等&#xff0c;那么能不能自动导入&#xff0c;不用我们每个页面都写呢&#xff1f;是没问题的&#xff0c;这里让他的小帮手来帮你减轻负担&#xff1a;他就…

【.net core】.net core 6.0添加WCF服务引用

在 .NET Core 6.0 (.NET 6) 中&#xff0c;调用 WCF 服务 是完全支持的&#xff0c;只要服务使用的是 basicHttpBinding 或类似 HTTP 协议的绑定&#xff08;如 wsHttpBinding&#xff0c;但不推荐&#xff09; .NET Core不支持 net.tcp,只能用http形式。 .net core调用WCF服务…

小结: js 在浏览器执行原理

浏览器多进程与多线程 现代浏览器的标签环境隔离主要通过多进程架构和多线程机制实现&#xff0c;以确保安全、性能和稳定性。以下是浏览器实现标签环境隔离的多进程和多线程交互架构的详细解析&#xff1a; ------------------- ------------------- -----------…

【实战篇】低代码报表开发——平台运营日报表的开发实录

前言 myBuilder的推广有段时间了&#xff0c;想开发个报表看看平台运营的情况。采用myBuilder强大的报表、数据交换模块功能&#xff0c;直接开干。 1. 报表指标思考与概要设计 首先是报表模块的概要设计&#xff0c;先构思一下&#xff0c;我希望报表能查看新用户注册、活跃…

使用Qt操作SQLite数据库

目录 一、开发成果二、环境配置与基础概念1. 引入SQL模块2. SQLite数据库特性三、数据库连接与操作流程1. 创建并连接数据库2. 执行SQL语句3. 查询与遍历数据四、进阶操作与最佳实践1. 事务处理2. 错误处理3. 使用模型/视图架构五、完整代码示例(学生人员管理)1.mainwindow.h…

ZYNQ笔记(二十):Clocking Wizard 动态配置

版本&#xff1a;Vivado2020.2&#xff08;Vitis&#xff09; 任务&#xff1a;ZYNQ PS端 通过 AXI4Lite 接口配置 Clocking Wizard IP核输出时钟频率 目录 一、介绍 二、寄存器定义 三、配置 四、PS端代码 一、介绍 Xilinx 的 Clock Wizard IP核 用于在 FPGA 中生成和管理…

探秘高可用负载均衡集群:企业网络架构的稳固基石

目录 高可用负载均衡集群 一、集群的本质与核心价值​ 二、高可用集群与负载均衡集群的定义​ 高可用集群&#xff08;HA Cluster&#xff09;​ 负载均衡集群&#xff08;Load Balance Cluster&#xff09;​ 三&#xff0e;高可用与负载均衡的完美融合 四&#xff0e;…

JAVA:ResponseBodyEmitter 实现异步流式推送的技术指南

1、简述 在许多场景下,我们希望后端能够以流式、实时的方式推送数据给前端,比如消息通知、日志实时展示、进度条更新等。Spring Boot 提供了 ResponseBodyEmitter 机制,可以让我们在 Controller 中异步地推送数据,从而实现实时流式输出。 样例代码:https://gitee.com/lh…

CSS- 1.1 css选择器

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在HBuilder中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 HTML系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查看&#xff01; 系…

ABP-Book Store Application中文讲解 - Part 2: The Book List Page

本章用于介绍如何创建Book List Page。 TBD 1. 汇总 ABP-Book Store Application中文讲解-汇总-CSDN博客 2. 前一章 ABP-Book Store Application中文讲解 - Part 1: Creating the Server Side 项目之间的引用关系。 目录 1. 多语言配置 1.1 zh-Hans.json 1.2 en.jso…

08 web 自动化之 PO 设计模式详解

文章目录 一、什么是 POM二、如何基于 POM 进行自动化框架架构&#xff1f;1、base 层封装2、pageobjects 层封装3、TestCases 层封装 三、元素和方法分离&数据分离1、哪些部分可以进行分离2、示例代码 四、总结 一、什么是 POM POM page object model 页面对象模型 WEB 自…

langchain4j集成QWen、Redis聊天记忆持久化

langchain4j实现聊天记忆默认是基于进程内存的方式&#xff0c;InMemoryChatMemoryStore是具体的实现了&#xff0c;是将聊天记录到一个map中&#xff0c;如果用户大的话&#xff0c;会造成内存溢出以及数据安全问题。位了解决这个问题 langchain4提供了ChatMemoryStore接口&am…