[DOC] Update README.md to enhance project overview and add performance insights for cross-language calls
🔗gitlink仓库
🔗Get Started
🔗本文档更新地址
🔗项目wiki
✅ 基于Socket接口编程
✅ 平台化,RegisterFactory快速注册服务
✅ 跨语言调用:支持两种跨语言调用方法:Python-C++ & Python-Java
✅ IDL & IDL Compiler
✅ 基于线程池的并发模型
上图演示了两个终端之间的远程调用:左侧终端运行了提供C++服务的Server,右侧终端Client通过python程序调用Server的C++服务。
上图演示了Client通过远程调用,监测Server的性能指标,包括CPU利用率,内存利用率和磁盘利用率。
本项目实现了一个基础功能完整的RPC(远程过程调用)框架,该框架完全基于Python Socket编程,包含两个子框架,分别支持Python-Java、Python-C++跨语言调用、IDL定义和高并发处理。此外,我们的框架具有平台化特性,能够动态注册和管理多种服务,并提供了详细的注释和示例,以方便后续完善和开发新的功能。
┌─────────────────────────────────────────────────────────────────┐ │ RPC Framework │ ├─────────────────────────────────────────────────────────────────┤ │ Client Layer │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ | │ │ Python │ │ Java │ | C++ | | │ │ Client │ │ Client │ | Client | | │ └─────────────┘ └─────────────┘ └─────────────┘ | ├─────────────────────────────────────────────────────────────────┤ │ Protocol Layer │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ RPC Protocol (Binary Header + JSON Body) ││ │ │ Magic(4) | Type(1) | Length(4) | JSON Message Body ││ │ └─────────────────────────────────────────────────────────────┘│ ├─────────────────────────────────────────────────────────────────┤ │ Server Layer │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Socket │ │ Service │ │ Thread │ │ │ │ Server │ │ Registry │ │ Pool │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ ├─────────────────────────────────────────────────────────────────┤ │ Tool Layer │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ IDL │ │ Code │ │ Serializer │ │ │ │ Compiler │ │ Generator │ │ Manager │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────────────┘
toyRPC/ ├── requirements.txt ├── src/ │ ├── RPC-CPP/ │ │ ├── cc/ │ │ │ ├── Calculator.hpp │ │ │ ├── Calculator.cc │ │ │ ├── functions.hpp │ │ │ ├── functions.cc │ │ │ ├── functions.pxd │ │ │ ├── functions.pyx │ │ │ ├── py_functions.py │ │ │ ├── setup.py │ │ │ ├── main.py │ │ │ └── README.md │ │ ├── idl/ │ │ │ ├── cpp_generator.py │ │ │ └── python_generator.py │ │ └── python/ │ │ ├── client.py │ │ ├── server.py │ │ ├── register.py │ │ ├── registerFactory.py │ │ └── Calculator.py │ ├── RPC-java/ │ │ └── rpc_framework/ │ │ ├── __init__.py │ │ ├── client.py │ │ ├── cross_language_adapter.py │ │ ├── idl_compiler.py │ │ ├── IDL编译器改进总结.md │ │ ├── serializer.py │ │ ├── registry.py │ │ ├── protocol.py │ │ └── server.py │ └── tests/ │ ├── example-provider-consumer/ │ │ ├── consumer.py │ │ └── provider.py │ ├── example-remote-monitor/ │ │ ├── monitor.py │ │ └── server.py │ ├── examples/ │ │ ├── example_client.py │ │ ├── example_idl.py │ │ ├── example_service.py │ │ ├── service.idl │ │ ├── test_idl_generate_java_client.py │ │ ├── test_incomplete_rpc_framework.py │ │ ├── test_simple_rpc_framework.py │ │ └── idl_test/ │ │ ├── python/ │ │ │ ├── test_cal.py │ │ │ ├── test_eq.py │ │ │ └── test_ut.py │ │ └── java/ │ │ ├── test_cal.java │ │ ├── test_eq.java │ │ └── test_ut.java │ └── example-idl/ │ ├── example_cc/ │ │ ├── idl_client.cpp │ │ └── idl_server.cpp │ └── example_python/ │ ├── client.py │ └── server.py
该项目分为Python-Java框架、Python-C++框架两个部分,以实现Python和Java、Python和C++两种不同的跨语言互操作性。其中src目录下,RPC-CPP和RPC-Java目录分别是两个不同子框架的核心RPC实现。tests目录下examples目录是Python-Java子框架的示例代码,其余example目录是Python-C++子框架的示例代码。
Python-Java子框架
该子框架在网络通信协议组件中,主要定义了RPC通信的消息类型、消息格式和协议处理功能。其中,消息类型由请求(REQUEST)、响应(RESPONSE)、错误(ERROR)和心跳(HEARTBEAT)4部分组成,主要作用是为协议通信提供类型约束。消息格式常量的定义如下表所示,由头部和消息体组成。头部开始是魔法数字(MAGIC:),用于协议识别和验证;然后是消息类型,用于识别消息的类型;最后是消息体的长度,便于消息体的正确处理。在该定义中,由于消息头部的长度是固定的,固定9字节头部(4+1+4),从而可以确保后续解析的可靠性;消息体采用JSON+二进制格式,提高了消息传输的效率以及可读性和扩展性。
+----------+----------+----------+----------+ | Magic(4) | Type(1) | Length(4)| Body(N) | +----------+----------+----------+----------+
在我们的具体实现过程中,主要包括了以下的核心方法:
Python-CPP子框架
该子框架客户端和服务端之间的消息格式直接基于JSON实现,没有复杂的头部信息,实现相对简单。消息发送方使用JSON进行消息的序列化和反序列化,将信息封装为JSON对象,然后调用Json::FastWriter将其转换为字符串,通过套接字进行传输。消息接收方接受到字符串数据后,再使用Json::Reader将字符串解析为 JSON 对象进行处理。 其主要包括的核心方法为:
在设计时,我们采用了策略模式和工厂模式,设计的消息序列化模块的核心功能是:
同时,我们在设计时考虑到了扩展性和灵活性,提供了序列化器基类,使得后续工作者可以自行配置选择合适的序列化方式,也支持多种序列化方式的无缝切换,方便在不同场景下使用。在我们的具体实现过程中,主要实现了以下的核心方法:
RPC-CPP 子框架的消息序列化模块通过 JSON 实现消息的序列化与反序列化,支持跨语言数据交换,并具备良好的扩展性与灵活性。主要实现了以下核心功能:
核心方法介绍如下:
Python—Java子框架
Python—Java子框架服务注册表实现了平台化的服务注册、发现和管理功能,具体功能包括但不限于:
我们在设计的过程中,采用了典型的“注册-发现-调用”模式,用以提供RPC框架的核心服务管理能力。在我们的具体实现过程中,主要实现了以下的核心方法:
这种设计也能体现课堂上,丁老师所传授的高内聚、低耦合的思想,从而为分布式服务提供了可靠的本地注册管理能力。在具体进行实验模拟时,注册表会经历注册、发现和调用三个阶段。在注册阶段,首先会创建一个服务存储字典,其中键为服务名称,值为ServiceInfo对象,用以后续的服务添加和调用;在发现阶段,通过服务名在字典中查找对应的ServiceInfo对象,并返回该对象;在调用阶段,首先会检查服务的状态,如果服务状态可用,则进行调用,否则返回错误信息。
Python-CPP 子框架的服务注册表模块实现了服务的注册、管理与调用功能,通过装饰器的方式简化服务注册流程,为客户端提供了便捷的服务发现与调用途径,确保了服务的集中管理和高效调用。其核心组件包含以下几类:
这种设计同样体现了高内聚、低耦合的思想,服务的注册、发现和调用功能相互独立,便于维护和扩展。服务的注册逻辑封装在Server类中,服务的发现和调用逻辑由客户端实现,降低了模块之间的耦合度。同时,每个模块内部的功能紧密相关,提高了内聚性。
在Python-Java子框架RPC服务端的设计过程中,我们主要采用了分层架构,将网络通信、协议处理、服务管理等功能进行了分离。在实现过程中,我们并未使用已有的开源架构,而是基于Python语言Socket库的TCP通信,主要实现了以下的核心方法:
此外,我们在类中还采用了并发处理策略。当主线程接受到新的连接时,服务端会创建一个ClientConnection对象,并将其添加到连接池中;然后,线程池会处理客户端请求,避免阻塞;最后,使用锁机制保护共享资源。
在Python-CPP 子框架服务端的设计中,同样采用了分层架构,将网络通信、服务注册与管理等功能进行了分离。该服务端基于 Python 语言的socket库实现 TCP 通信,主要实现了以下核心方法:
Python-CPP子框架的服务端也采用了并发处理策略来提高性能。当主线程接收到新的客户端连接时,会为该连接创建一个新的线程,调用__handle__方法处理该客户端的请求。这样可以避免单个客户端的请求阻塞其他客户端的处理。同时,使用Lock机制保护_instance变量,确保Server类的单例模式在多线程环境下的线程安全。
在RPC客户端中,我们设计了远程方法调用接口。在设计过程中,我们通过UUID生成唯一请求ID,建立请求与响应的对应关系,并使用RLock保护共享资源,支持多线程并发调用,此外,我们还设计了异步通信模式,使用独立的接收线程处理服务器响应,主线程通过事件机制等待结果。在实现过程中,我们主要实现了以下的核心方法:
由于我们的设计结构实现了发送和接收分离,在实际运行过程中,大大提高了并发性能,减少了冗余程序带来的性能降低影响。
在Python-CPP子框架客户端的设计中,我们实现了远程方法调用接口,通过 UUID 生成唯一请求 ID 建立请求与响应的对应关系,并使用线程安全机制支持多线程并发调用。客户端采用发送和接收分离的设计模式,提升了并发性能。以下是核心方法的介绍:
Python-CPP子框架客户端通过高效的设计和实现,提供了简洁而强大的远程方法调用能力,同时确保了在多线程环境下的性能和稳定性。
跨语言适配器主要用来处理不同编程语言间的数据类型转换和协议适配,主要通过统一的抽象层来屏蔽不同编程语言间的差异。在设计时,我们采用了课上老师所教导的思路,使用枚举法和映射表来进行实现,在后续增加序列化格式时,我们发现了这种设计方法具有较高的可扩展性,很方便后续添加新的语言支持。我们的跨语言适配器具有:自动数据类型转换、Java和Python两种序列化格式支持以及语言特定的协议适配三种功能。在实现过程中,我们主要实现了以下的核心方法:
在跨语言调用测试中,因为不同语言的协议具有差异,如Java的jsonrpc字段,所以我们还创建了协议处理器,针对性地为不同语言创建特定格式的请求/响应,从而屏蔽了繁杂的处理细节。
Python-CPP 子框架的跨语言适配器通过统一的接口设计和类型映射机制,实现了不同编程语言间的数据类型转换和协议适配。虽然代码结构较 Python-Java 子框架更为精简,但核心功能依然完整:
IDL编译器主要用于将IDL定义自动地转换为RPC客户端代码,该过程应该是一步到位的,即客户端可直接运行生成的代码,用来调用服务端相应注册的服务。我们在设计的过程中,认识到IDL编译器本质是一个接口描述语言,因此选择了模块化的方式,将解析与生成过程进行分离,这样也易于扩展新语言。对于解析过程,我们使用正则表达式来对IDL语法进行解析;对于生成过程,我们采用“固定样板+具体方法”的生成模式。当解析过后,对于导入函数库、连接方法等固定功能,使用固定的模板进行内容的生成;对于具体的功能,则使用特定的函数进行生成。在实现过程中,我们主要实现了以下的核心方法:
DataType:定义了数据类型枚举
Parameter: 函数参数的定义
Method: 方法的定义
Service: 服务的定义
IDLCompiler:IDL编译器的核心类,包括解析层和代码生成层:
在测试中,对于Python客户端,由于其语言和服务端相同,很容易便通过了相应的测试;对于Java客户端,我们在测试过程中遇到了较多问题,最大的问题便是消息的格式问题,原因是刚开始对于Java代码的生成,IDL编译器仅能支持Json格式,而服务端是基于二进制格式,为此我们对IDL编译器中的_generate_java_client方法进行了修改,使其能够支持二进制格式。IDL编译器历经三次版本的迭代,具体迭代的内容请参考 IDL编译器改进总结。
IDL 编译器在 Python-CPP 子框架中承担着将 IDL 定义自动转换为 RPC 客户端代码的重要任务,其目标是让客户端能够直接运行生成的代码来调用服务端注册的服务。为了实现良好的扩展性,我们采用模块化设计,将解析过程和代码生成过程分离。解析过程借助正则表达式对 IDL 语法进行分析,生成过程则采用 “固定样板 + 具体方法” 的模式,对于导入函数库、建立连接等固定功能,使用预设模板生成代码,对于具体功能则通过特定函数生成。
两个RPC子框架下的并发模型均充分利用了线程池和线程安全设计,支持服务端和客户端的多线程并发处理需求,具备良好的性能、可维护性和基础稳定性。模型简洁,专注于线程池管理、连接清理、请求映射和超时控制等核心能力。下面将从服务器并发和客户端并发两个方面进行介绍。
本节介绍如何启动Python-Java子框架服务端与客户端,并进行基本功能与高级功能测试。
1.启动服务器
运行以下命令启动服务端:
python ./examples/example_service.py
运行命令后,服务端将在 localhost:8888 启动,并注册以下服务:
2.启动客户端
2.1 简单的RPC客户端测试
python ./examples/test_simple_rpc_framework.py
该测试将创建并注册一系列客户端服务实例,包括
2.2 完整的RPC客户端测试
python ./examples/test_incomplete_rpc_framework.py
此命令将执行多个功能测试,包括:
2.3 全面的RPC客户端测试
python ./examples/example_client.py
执行该命令后,客户端将创建并测试与服务端对应的完整服务集:
本节介绍如何启动Python-CPP子框架服务端与客户端,并进行基本功能与高级功能测试。
1.安装扩展
cd toyrpc pip install -r requirements.txt cd src/RPC-CPP/cc python setup.py build_ext --inplace
2.配置环境
Windows:
$ export PYTHONPATH="\path\to\your\tpc-project\src;$PYTHONPATH"
Linux:
export PYTHONPATH="/your/path:$PYTHONPATH" $ source ~/.bashrc
3.启动服务端
cd src/tests/example-provider-consumer python provider.py
4.启动客户端
cd src/tests/example-provider-consumer python consumer.py
本节介绍如何使用Python-Java子框架内置IDL编译器,生成跨语言RPC客户端代码。
1.IDL语法示例
service CalculatorService { version "1.0.0" description "A simple calculator service" method add(a: float, b: float) -> float { description "Add two numbers" } method factorial(n: int) -> int { description "Calculate factorial" } method hello(name: string = "World") -> string { description "Generate greeting with default parameter" } }
2.使用IDL编译器生成客户端代码
2.1 生成Java客户端服务代码
python ./examples/test_idl_generate_java_client.py
该IDL会生成Java语言EquationService服务,运行命令后,将执行下面四个步骤:
2.2 生成多语言客户端代码
python ./examples/example_idl.py
运行命令后,将执行下面四个步骤:
3.跨语言调用测试
3.1 Python客户端测试
首先,确保服务端已启动,用于注册服务:
然后运行IDL编译器:
最后运行各类服务测试脚本:
python ./examples/idl_test/python/test_cal.py
python ./examples/idl_test/python/test_eq.py
python ./examples/idl_test/python/test_ut.py
3.2 Java客户端测试
首先,启动服务端并注册服务:
python examples/calculator_service.py
其次,执行IDL编译器生成Java客户端:
python examples/idl_demo.py
然后,编译生成的客户端代码:
javac ./examples/generated/CalculatorServiceClient.java javac ./examples/generated/EquationServiceClient.java javac ./examples/generated/UtilityServiceClient.java
然后,编译测试代码:
javac -cp "./examples/generated" ./examples/idl_test/java/test_cal.java javac -cp "./examples/generated" ./examples/idl_test/java/test_eq.java javac -cp "./examples/generated" ./examples/idl_test/java/test_ut.java
最后,运行各类服务测试脚本:
java -cp "examples/idl_test/java/;./examples/generated" test_cal
java -cp "examples/idl_test/java/;./examples/generated" test_eq
java -cp "examples/idl_test/java/;./examples/generated" test_ut
编译型语言对比解释型语言,天然具有性能优势。相同时间复杂度的算法,C++版本要比Python版本快十倍至百倍不等。因此,现代RPC架构中,对于计算密集型的服务,Server提供Client C++服务,允许Client以Python或其他解释型语言(user-friendly)调用,从而达到用户友好&极致性能的效果。
对于计算密集型任务,toyRPC在Server端提供任务的C++高性能实现,支持Client在python程序中调用Server端的C++函数。 toyRPC目前实现了getNPrimes(获取前N个质数),getNFibonacci(获取前N个斐布拉契数),matmul(矩阵乘法),以下为跨语言调用相较于单语言调用(Server,Client统一使用Python)带来的性能收益。横轴为算法规模$N$,纵轴为服务执行时间(单位为毫秒)。三个任务均达到了好的性能收益,特别在矩阵乘法任务中,$N = 256$时,获得了平均600+倍的性能收益。
getNPrimes
getNFibonacci
matmul
本项目以“从零构建 RPC 框架”为目标,系统实现了两个RPC子框架,构建了一个具备跨语言调用能力的远程过程调用系统,涵盖了服务注册、序列化传输、协议解析、并发处理、IDL 编译等核心模块,最终形成了具备工程完整度和教学演示价值的通用型 RPC 框架。
未来的改进方向包括:
总体而言,本项目不仅完成了一个具备完整通信与调度能力的 RPC 系统,两个不同的子框架分别支持Python-Java、Python-C++两种跨语言交互,更在模块设计、协议构建、跨语言支持等方面进行了系统性探索,积累了宝贵的工程经验,为进一步开发更复杂、高性能、高可用的分布式系统奠定了坚实基础。
A minimalist cross-language RPC framework supporting Python <-> C++ interoperability.
©Copyright 2023 CCF 开源发展委员会 Powered by Trustie& IntelliDE 京ICP备13000930号
RPC Framework 项目报告
Overview
🔗gitlink仓库
🔗Get Started
🔗本文档更新地址
🔗项目wiki
Features
✅ 基于Socket接口编程
✅ 平台化,RegisterFactory快速注册服务
✅ 跨语言调用:支持两种跨语言调用方法:Python-C++ & Python-Java
✅ IDL & IDL Compiler
✅ 基于线程池的并发模型
演示
python-c++远程调用
上图演示了两个终端之间的远程调用:左侧终端运行了提供C++服务的Server,右侧终端Client通过python程序调用Server的C++服务。
上图演示了Client通过远程调用,监测Server的性能指标,包括CPU利用率,内存利用率和磁盘利用率。
项目概述
本项目实现了一个基础功能完整的RPC(远程过程调用)框架,该框架完全基于Python Socket编程,包含两个子框架,分别支持Python-Java、Python-C++跨语言调用、IDL定义和高并发处理。此外,我们的框架具有平台化特性,能够动态注册和管理多种服务,并提供了详细的注释和示例,以方便后续完善和开发新的功能。
设计目标与原则
设计目标
设计原则
系统架构设计
整体架构图
项目结构
该项目分为Python-Java框架、Python-C++框架两个部分,以实现Python和Java、Python和C++两种不同的跨语言互操作性。其中src目录下,RPC-CPP和RPC-Java目录分别是两个不同子框架的核心RPC实现。tests目录下examples目录是Python-Java子框架的示例代码,其余example目录是Python-C++子框架的示例代码。
组件实现
网络通信协议
Python-Java子框架
该子框架在网络通信协议组件中,主要定义了RPC通信的消息类型、消息格式和协议处理功能。其中,消息类型由请求(REQUEST)、响应(RESPONSE)、错误(ERROR)和心跳(HEARTBEAT)4部分组成,主要作用是为协议通信提供类型约束。消息格式常量的定义如下表所示,由头部和消息体组成。头部开始是魔法数字(MAGIC:),用于协议识别和验证;然后是消息类型,用于识别消息的类型;最后是消息体的长度,便于消息体的正确处理。在该定义中,由于消息头部的长度是固定的,固定9字节头部(4+1+4),从而可以确保后续解析的可靠性;消息体采用JSON+二进制格式,提高了消息传输的效率以及可读性和扩展性。
在我们的具体实现过程中,主要包括了以下的核心方法:
Python-CPP子框架
该子框架客户端和服务端之间的消息格式直接基于JSON实现,没有复杂的头部信息,实现相对简单。消息发送方使用JSON进行消息的序列化和反序列化,将信息封装为JSON对象,然后调用Json::FastWriter将其转换为字符串,通过套接字进行传输。消息接收方接受到字符串数据后,再使用Json::Reader将字符串解析为 JSON 对象进行处理。 其主要包括的核心方法为:
消息序列化模块
Python-Java子框架
在设计时,我们采用了策略模式和工厂模式,设计的消息序列化模块的核心功能是:
同时,我们在设计时考虑到了扩展性和灵活性,提供了序列化器基类,使得后续工作者可以自行配置选择合适的序列化方式,也支持多种序列化方式的无缝切换,方便在不同场景下使用。在我们的具体实现过程中,主要实现了以下的核心方法:
Python-CPP子框架
RPC-CPP 子框架的消息序列化模块通过 JSON 实现消息的序列化与反序列化,支持跨语言数据交换,并具备良好的扩展性与灵活性。主要实现了以下核心功能:
核心方法介绍如下:
服务注册表
Python—Java子框架
Python—Java子框架服务注册表实现了平台化的服务注册、发现和管理功能,具体功能包括但不限于:
我们在设计的过程中,采用了典型的“注册-发现-调用”模式,用以提供RPC框架的核心服务管理能力。在我们的具体实现过程中,主要实现了以下的核心方法:
这种设计也能体现课堂上,丁老师所传授的高内聚、低耦合的思想,从而为分布式服务提供了可靠的本地注册管理能力。在具体进行实验模拟时,注册表会经历注册、发现和调用三个阶段。在注册阶段,首先会创建一个服务存储字典,其中键为服务名称,值为ServiceInfo对象,用以后续的服务添加和调用;在发现阶段,通过服务名在字典中查找对应的ServiceInfo对象,并返回该对象;在调用阶段,首先会检查服务的状态,如果服务状态可用,则进行调用,否则返回错误信息。
Python-CPP子框架
Python-CPP 子框架的服务注册表模块实现了服务的注册、管理与调用功能,通过装饰器的方式简化服务注册流程,为客户端提供了便捷的服务发现与调用途径,确保了服务的集中管理和高效调用。其核心组件包含以下几类:
这种设计同样体现了高内聚、低耦合的思想,服务的注册、发现和调用功能相互独立,便于维护和扩展。服务的注册逻辑封装在Server类中,服务的发现和调用逻辑由客户端实现,降低了模块之间的耦合度。同时,每个模块内部的功能紧密相关,提高了内聚性。
服务端
Python-Java子框架
在Python-Java子框架RPC服务端的设计过程中,我们主要采用了分层架构,将网络通信、协议处理、服务管理等功能进行了分离。在实现过程中,我们并未使用已有的开源架构,而是基于Python语言Socket库的TCP通信,主要实现了以下的核心方法:
此外,我们在类中还采用了并发处理策略。当主线程接受到新的连接时,服务端会创建一个ClientConnection对象,并将其添加到连接池中;然后,线程池会处理客户端请求,避免阻塞;最后,使用锁机制保护共享资源。
Python-CPP子框架
在Python-CPP 子框架服务端的设计中,同样采用了分层架构,将网络通信、服务注册与管理等功能进行了分离。该服务端基于 Python 语言的socket库实现 TCP 通信,主要实现了以下核心方法:
Python-CPP子框架的服务端也采用了并发处理策略来提高性能。当主线程接收到新的客户端连接时,会为该连接创建一个新的线程,调用__handle__方法处理该客户端的请求。这样可以避免单个客户端的请求阻塞其他客户端的处理。同时,使用Lock机制保护_instance变量,确保Server类的单例模式在多线程环境下的线程安全。
客户端
Python-Java子框架
在RPC客户端中,我们设计了远程方法调用接口。在设计过程中,我们通过UUID生成唯一请求ID,建立请求与响应的对应关系,并使用RLock保护共享资源,支持多线程并发调用,此外,我们还设计了异步通信模式,使用独立的接收线程处理服务器响应,主线程通过事件机制等待结果。在实现过程中,我们主要实现了以下的核心方法:
由于我们的设计结构实现了发送和接收分离,在实际运行过程中,大大提高了并发性能,减少了冗余程序带来的性能降低影响。
Python-CPP子框架
在Python-CPP子框架客户端的设计中,我们实现了远程方法调用接口,通过 UUID 生成唯一请求 ID 建立请求与响应的对应关系,并使用线程安全机制支持多线程并发调用。客户端采用发送和接收分离的设计模式,提升了并发性能。以下是核心方法的介绍:
Python-CPP子框架客户端通过高效的设计和实现,提供了简洁而强大的远程方法调用能力,同时确保了在多线程环境下的性能和稳定性。
跨语言适配器
Python-Java子框架
跨语言适配器主要用来处理不同编程语言间的数据类型转换和协议适配,主要通过统一的抽象层来屏蔽不同编程语言间的差异。在设计时,我们采用了课上老师所教导的思路,使用枚举法和映射表来进行实现,在后续增加序列化格式时,我们发现了这种设计方法具有较高的可扩展性,很方便后续添加新的语言支持。我们的跨语言适配器具有:自动数据类型转换、Java和Python两种序列化格式支持以及语言特定的协议适配三种功能。在实现过程中,我们主要实现了以下的核心方法:
在跨语言调用测试中,因为不同语言的协议具有差异,如Java的jsonrpc字段,所以我们还创建了协议处理器,针对性地为不同语言创建特定格式的请求/响应,从而屏蔽了繁杂的处理细节。
Python-CPP子框架
Python-CPP 子框架的跨语言适配器通过统一的接口设计和类型映射机制,实现了不同编程语言间的数据类型转换和协议适配。虽然代码结构较 Python-Java 子框架更为精简,但核心功能依然完整:
IDL编译器
Python-Java子框架
IDL编译器主要用于将IDL定义自动地转换为RPC客户端代码,该过程应该是一步到位的,即客户端可直接运行生成的代码,用来调用服务端相应注册的服务。我们在设计的过程中,认识到IDL编译器本质是一个接口描述语言,因此选择了模块化的方式,将解析与生成过程进行分离,这样也易于扩展新语言。对于解析过程,我们使用正则表达式来对IDL语法进行解析;对于生成过程,我们采用“固定样板+具体方法”的生成模式。当解析过后,对于导入函数库、连接方法等固定功能,使用固定的模板进行内容的生成;对于具体的功能,则使用特定的函数进行生成。在实现过程中,我们主要实现了以下的核心方法:
DataType:定义了数据类型枚举
Parameter: 函数参数的定义
Method: 方法的定义
Service: 服务的定义
IDLCompiler:IDL编译器的核心类,包括解析层和代码生成层:
在测试中,对于Python客户端,由于其语言和服务端相同,很容易便通过了相应的测试;对于Java客户端,我们在测试过程中遇到了较多问题,最大的问题便是消息的格式问题,原因是刚开始对于Java代码的生成,IDL编译器仅能支持Json格式,而服务端是基于二进制格式,为此我们对IDL编译器中的_generate_java_client方法进行了修改,使其能够支持二进制格式。IDL编译器历经三次版本的迭代,具体迭代的内容请参考 IDL编译器改进总结。
Python-CPP子框架
IDL 编译器在 Python-CPP 子框架中承担着将 IDL 定义自动转换为 RPC 客户端代码的重要任务,其目标是让客户端能够直接运行生成的代码来调用服务端注册的服务。为了实现良好的扩展性,我们采用模块化设计,将解析过程和代码生成过程分离。解析过程借助正则表达式对 IDL 语法进行分析,生成过程则采用 “固定样板 + 具体方法” 的模式,对于导入函数库、建立连接等固定功能,使用预设模板生成代码,对于具体功能则通过特定函数生成。
并发模型
两个RPC子框架下的并发模型均充分利用了线程池和线程安全设计,支持服务端和客户端的多线程并发处理需求,具备良好的性能、可维护性和基础稳定性。模型简洁,专注于线程池管理、连接清理、请求映射和超时控制等核心能力。下面将从服务器并发和客户端并发两个方面进行介绍。
服务器并发
客户端并发
操作说明
Python-Java子框架快速开始
本节介绍如何启动Python-Java子框架服务端与客户端,并进行基本功能与高级功能测试。
1.启动服务器
运行以下命令启动服务端:
运行命令后,服务端将在 localhost:8888 启动,并注册以下服务:
2.启动客户端
2.1 简单的RPC客户端测试
该测试将创建并注册一系列客户端服务实例,包括
2.2 完整的RPC客户端测试
此命令将执行多个功能测试,包括:
2.3 全面的RPC客户端测试
执行该命令后,客户端将创建并测试与服务端对应的完整服务集:
Python-CPP子框架快速开始
本节介绍如何启动Python-CPP子框架服务端与客户端,并进行基本功能与高级功能测试。
1.安装扩展
2.配置环境
Windows:
Linux:
3.启动服务端
4.启动客户端
Python-Java子框架接口描述语言(IDL)使用说明
本节介绍如何使用Python-Java子框架内置IDL编译器,生成跨语言RPC客户端代码。
1.IDL语法示例
2.使用IDL编译器生成客户端代码
2.1 生成Java客户端服务代码
该IDL会生成Java语言EquationService服务,运行命令后,将执行下面四个步骤:
2.2 生成多语言客户端代码
运行命令后,将执行下面四个步骤:
3.跨语言调用测试
3.1 Python客户端测试
首先,确保服务端已启动,用于注册服务:
然后运行IDL编译器:
最后运行各类服务测试脚本:
3.2 Java客户端测试
首先,启动服务端并注册服务:
其次,执行IDL编译器生成Java客户端:
然后,编译生成的客户端代码:
然后,编译测试代码:
最后,运行各类服务测试脚本:
性能分析
性能特点
跨语言调用性能增益(Python-C++)
编译型语言对比解释型语言,天然具有性能优势。相同时间复杂度的算法,C++版本要比Python版本快十倍至百倍不等。因此,现代RPC架构中,对于计算密集型的服务,Server提供Client C++服务,允许Client以Python或其他解释型语言(user-friendly)调用,从而达到用户友好&极致性能的效果。
对于计算密集型任务,toyRPC在Server端提供任务的C++高性能实现,支持Client在python程序中调用Server端的C++函数。 toyRPC目前实现了
getNPrimes
(获取前N个质数),getNFibonacci
(获取前N个斐布拉契数),matmul
(矩阵乘法),以下为跨语言调用相较于单语言调用(Server,Client统一使用Python)带来的性能收益。横轴为算法规模$N$,纵轴为服务执行时间(单位为毫秒)。三个任务均达到了好的性能收益,特别在矩阵乘法任务中,$N = 256$时,获得了平均600+倍的性能收益。当前限制
项目总结
本项目以“从零构建 RPC 框架”为目标,系统实现了两个RPC子框架,构建了一个具备跨语言调用能力的远程过程调用系统,涵盖了服务注册、序列化传输、协议解析、并发处理、IDL 编译等核心模块,最终形成了具备工程完整度和教学演示价值的通用型 RPC 框架。
项目成果概括
技术亮点与工程积累
工程价值与适用场景
该框架适用于以下典型场景:当前局限与未来展望
尽管本项目实现了基础 RPC 能力,但当前仍存在以下局限性:未来的改进方向包括:
总体而言,本项目不仅完成了一个具备完整通信与调度能力的 RPC 系统,两个不同的子框架分别支持Python-Java、Python-C++两种跨语言交互,更在模块设计、协议构建、跨语言支持等方面进行了系统性探索,积累了宝贵的工程经验,为进一步开发更复杂、高性能、高可用的分布式系统奠定了坚实基础。