【分布式理论六】分布式调用(4):服务间的远程调用(RPC)

news2025/2/8 0:03:31

文章目录

    • 一、RPC 调用过程
    • 二、RPC 动态代理:屏蔽远程通讯细节
      • 1. 动态代理示例
      • 2. 如何将动态代理应用于 RPC
    • 三、RPC 序列化
    • 四、RPC 协议编码
      • 1. 协议编码的作用
      • 2. RPC 协议消息组成
    • 五、RPC 网络传输
      • 1. 网络传输流程
      • 2. 关键优化点

一、RPC 调用过程

RPC(Remote Procedure Call,远程过程调用)是一种让不同网络节点上的服务相互调用的技术。它的核心目标是屏蔽远程调用的复杂性,使远程服务的调用方式如同本地调用一样简单。在分布式系统中,RPC 通过封装底层网络通信细节,提高了服务调用的可用性和开发效率。

RPC 调用流程包括:

在这里插入图片描述

  1. 动态代理:客户端通过代理对象调用远程方法。
  2. 序列化:将请求数据转换为二进制格式,便于传输。
  3. 协议编码:增加数据包的协议标识和长度信息。
  4. 网络传输:通过网络传递数据包。
  5. 协议解码:服务端解析请求包。
  6. 反序列化:将二进制数据转换回原始对象。
  7. 执行方法:调用对应的远程方法并处理请求。
  8. 响应返回:按照相同的序列化、网络传输等流程将响应结果返回给调用方。

 

二、RPC 动态代理:屏蔽远程通讯细节

动态代理(Dynamic Proxy)是 Java 提供的一种机制,允许在运行时动态创建代理对象,拦截方法调用,并在调用前后执行额外的逻辑。

在 RPC 场景中,动态代理的主要作用是屏蔽底层的远程通信细节,让客户端可以像调用本地方法一样调用远程服务。

1. 动态代理示例

示例代码:

public interface ServerProvider {
    void sayHello(String str);
}

public class ServerProviderImpl implements ServerProvider {
    @Override
    public void sayHello(String str) {
        System.out.println("Hello " + str);
    }
}

import java.lang.reflect.*;


/**
- `DynamicProxy` 实现了 `InvocationHandler`,用于拦截方法调用并执行代理逻辑。
- `invoke` 方法中:
    1. `method.invoke(realObject, args);` 通过反射调用真实对象的方法。
*/
public class DynamicProxy implements InvocationHandler {
    private Object realObject;
    
    public DynamicProxy(Object object) {
        this.realObject = object;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(realObject, args);
    }
}

public class Client {
    public static void main(String[] args) {
        ServerProvider realServer = new ServerProviderImpl();
        InvocationHandler handler = new DynamicProxy(realServer);
        ServerProvider proxyInstance = (ServerProvider) Proxy.newProxyInstance(
            handler.getClass().getClassLoader(),
            realServer.getClass().getInterfaces(),
            handler);
        proxyInstance.sayHello("world");
    }
}

通过动态代理,客户端不直接依赖于 ServerProviderImpl,而是通过接口和代理类进行调用,这样:

  • 解耦了客户端和服务端,不需要在客户端硬编码调用远程方法。
  • 方便在代理类中加入 RPC 逻辑,比如序列化、网络传输等。
  • 增强扩展性,可以在 invoke 方法中添加日志、权限校验、负载均衡等功能。

 

2. 如何将动态代理应用于 RPC

(1) 在代理类中加入远程调用逻辑
(2) 客户端使用代理调用远程服务

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 1. 构造 RPC 请求
    RpcRequest request = new RpcRequest();
    request.setMethodName(method.getName());
    request.setParameters(args);

    // 2. 发送请求到远程服务
    RpcResponse response = RpcClient.sendRequest(request);

    // 3. 解析响应并返回结果
    return response.getResult();
}


ServerProvider serverProvider = (ServerProvider) Proxy.newProxyInstance(
    getClass().getClassLoader(),
    new Class[]{ServerProvider.class},
    new RpcDynamicProxy("http://remote-server")
);
serverProvider.sayHello("world");

如果想进一步实现 RPC 的完整流程,可以加入序列化、网络传输、反序列化等模块,搭建一个真正的 RPC 组件!

 

三、RPC 序列化

序列化是将对象转换成字节流的过程,而反序列化则是恢复对象的过程。常见的序列化方式包括:

  • JSON:易读易用,但额外空间开销较大。
  • Hessian:二进制格式,序列化后字节数小,性能优于 JSON。
  • Protobuf:高效、跨语言支持,适用于大规模分布式应用。
  • Thrift:Facebook 开源的高效序列化框架,结合了 RPC 服务框架。

 

四、RPC 协议编码

1. 协议编码的作用

有了序列化功能,就可以将客户端的请求对象转化成字节流在网络上传输了,这个字节流转换为二进制信息以后会写入本地的 Socket 中,然后通过网卡发送到服务端。从编程角度来看,每次请求只会发送一个请求包,但是从网络传输的角度来看,网络传输过程中会将二进制包拆分成很多个数据包,这一点也可以从 TCP 传输数据的原理看出。拆分后的多个二进制包会同时发往服务端,服务端接收到这些数据包以后,将它们合并到一起,再进行反序列化以及后面的操作。

实际上,协议编码要做的事情就是对同一次网络请求的数据包进行拆分,并且为拆分得到的每个数据包定义边界、长度等信息。

 

2. RPC 协议消息组成

RPC 协议消息由 消息头消息体 组成:

  • 消息头 包含协议标识、数据长度、请求类型等信息。
  • 消息体 是序列化后的数据。

协议编码的核心目标是确保数据包正确地分片、合并,并提供必要的描述信息,保障网络传输的可靠性。

消息头部分主要存放消息本身的描述信息,如图所示。
在这里插入图片描述

名称描述
魔术位(magic)协议魔术,为解码设计
消息头长度(header size)用来描述消息头长度,为扩展设计
协议版本(version)协议版本,用于版本兼容
消息体序列化类型(st)描述消息体的序列化类型,例如 JSON、gRPC
心跳标记(hb)每次传输都会建立一个长连接,隔一段时间向接收方发送一次心跳请求,保证对方始终在线
单向消息标记(ow)标记是否为单向消息
响应消息标记(rp)用来标记是请求消息还是响应消息
响应消息状态码(status code)标记响应消息状态码
保留字段(reserved)用于填充消息,保证消息的字节是对齐的
消息 Id(message id)用来唯一确定一个消息的标识
消息体长度(body size)描述消息体的长度

 

五、RPC 网络传输

1. 网络传输流程

在 RPC 调用中,服务调用方(Client)需要发送请求给服务提供方(Server),然后等待服务器处理并返回响应数据。在这个过程中,数据在应用程序、操作系统内核、网络传输三个层次之间流动,并涉及多个数据复制操作。

从示意图中可以看出,数据的流转主要分为两部分:

  • 请求发送过程(客户端 -> 服务器)
  • 响应接收过程(服务器 -> 客户端)

在这里插入图片描述

 

对于请求发送流程(Client -> Server),服务调用方(Client)发起 RPC 请求,其数据流动过程如下:

步骤操作数据位置
1应用程序写入数据业务代码执行RPC调用,将数据写入应用缓冲区(User Space)
2数据复制到内核缓冲区操作系统将应用缓冲区的数据复制到内核缓冲区(Kernel Space)
3通过网络发送数据从内核缓冲区被传输到网卡(Network Card),并通过网络协议(如TCP)拆分成数据包发送到远程服务器
4服务器接收数据服务器端网卡接收数据包,并将其存入内核缓冲区
5数据复制到应用缓冲区服务器的内核将数据复制到应用缓冲区
6应用程序读取数据服务器端应用程序从应用缓冲区中获取数据,执行请求逻辑(如数据库查询、业务处理)

响应接收流程(Server -> Client):服务提供方(Server)处理完请求后,将结果返回给客户端,数据流动过程如下:

步骤操作具体内容
7应用程序写入数据服务器应用程序生成响应数据,并写入应用缓冲区
8数据复制到内核缓冲区服务器操作系统将数据从应用缓冲区复制到内核缓冲区,准备发送
9通过网络发送服务器的网卡将数据包发送到客户端
10客户端接收数据客户端网卡接收数据包,操作系统将其存入内核缓冲区
11数据复制到应用缓冲区数据从内核缓冲区复制到应用缓冲区,以便应用程序使用
12应用程序读取数据客户端应用程序从应用缓冲区中获取响应数据,完成 RPC 调用

 

2. 关键优化点

RPC 网络传输过程涉及多个阶段,包括数据在应用缓冲区、内核缓冲区、网络传输中的流转。优化 RPC 传输的关键在于减少数据复制、优化网络通信、使用异步 I/O 机制,提高整体性能。

操作具体内容
减少数据复制采用 零拷贝(Zero-Copy) 技术,如 mmapsendfile,避免数据在用户态和内核态之间频繁复制
优化网络传输使用 长连接(Keep-Alive) 避免频繁建立 TCP 连接;采用 批量发送数据压缩 来减少数据传输的开销
异步 I/O 处理使用 异步 I/O(如 Netty、epoll),避免同步阻塞,提高并发处理能力
优化缓冲区管理采用 池化缓冲区(Buffer Pool) 避免频繁申请和释放内存

 

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

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

相关文章

机器学习-线性回归(参数估计之结构风险最小化)

前面我们已经了解过关于机器学习中的结构风险最小化准则,包括L1 正则化(Lasso)、L2 正则化(Ridge)、Elastic Net,现在我们结合线性回归的场景,来了解一下线性回归的结构风险最小化,通…

IM 即时通讯系统-46-OpenIM 提供了专为开发者设计的开源即时通讯解决方案

IM 开源系列 IM 即时通讯系统-41-开源 野火IM 专注于即时通讯实时音视频技术,提供优质可控的IMRTC能力 IM 即时通讯系统-42-基于netty实现的IM服务端,提供客户端jar包,可集成自己的登录系统 IM 即时通讯系统-43-简单的仿QQ聊天安卓APP IM 即时通讯系统-44-仿QQ即…

node.js使用mysql2对接数据库

一、引言 在现代Web开发中,Node.js作为一种高效、轻量级的JavaScript运行时环境,已经广泛应用于后端服务的开发中。而MySQL,作为一个广泛使用的关系型数据库管理系统(RDBMS),提供了强大的数据存储和查询功能…

SQL/Panda映射关系

Pandas教程(非常详细)_pandas 教程-CSDN博客 SQL:使用SELECT col_1, col_2 FROM tab; Pandas:使用df[[col_1, col_2]]。 SQL:使用SELECT * FROM tab WHERE col_1 11 AND col_2 > 5; Pandas:使用df…

windows同时安装两个不同版本的Mysql

文章目录 目录 ?文章目录 前言 一、MySql下载 1、 官网下载: 2、 解压文件 3、 新建my.ini文件。 二、配置MySql环境变量 1、新建系统环境变量 ?三、MySql安装 1、进入MySql的bin目录 ?2、安装MySql服务 3、修改登录密码、并自动创建data文件夹 4、…

Docker最佳实践:安装Nacos

文章目录 Docker最佳实践:安装Nacos一、引言二、安装 Nacos1、拉取 Nacos Docker 镜像2、启动 Nacos 容器 三、配置 Nacos(可选)四、使用示例1、服务注册2、服务发现 五、总结 Docker最佳实践:安装Nacos 一、引言 Nacos 是阿里巴…

【deepseek实战】绿色好用,不断网

前言 最佳deepseek火热网络,我也开发一款windows的电脑端,接入了deepseek,基本是复刻了网页端,还加入一些特色功能。 助力国内AI,发出自己的热量 说一下开发过程和内容的使用吧。 目录 一、介绍 二、具体工作 1.1、引…

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之修改密码和个人资料

🧸安清h:个人主页 🎥个人专栏:【Spring篇】【计算机网络】【Mybatis篇】 🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。 目录 🎃1.修改密码 -持久…

3.攻防世界 weak_auth

题目描述提示 是一个登录界面,需要密码登录 进入题目页面如下 弱口令密码爆破 用1 or 1 #试试 提示用admin登录 则尝试 用户名admin密码:123456 直接得到flag 常用弱口令密码(可复制) 用户名 admin admin-- admin or -- admin…

『Apisix进阶篇』结合Consul作服务发现实战演练

文章目录 一、引言二、APISIX与Consul集成2.1 环境准备2.2 配置Consul服务发现2.2.1 修改APISIX配置文件2.2.2 重启APISIX 2.3 在路由中使用Consul服务发现2.3.1 创建路由2.3.2 验证路由 2.4 高级配置2.4.1 服务过滤2.4.2 多数据中心支持 三、总结 📣读完这篇文章里…

家用报警器的UML 设计及其在C++和VxWorks 上的实现01

M.W.Richardson 著,liuweiw 译 论文描述了如何运用 UML(统一建模语言)设计一个简单的家用报警器,并实现到 VxWorks 操作系统上。本文分两个部分,第一部分描述了如何用 UML 设计和验证家用报警器的模型,以使…

BUU24 [GXYCTF2019]BabyUpload 1

开局上传文件 上传muma.php 上传.htaccess文件也被打回 再次求助互联网,才发现这提示给的多么明显,上传.htaccess文件是检查文件类型(Contnet-Type),上传muma.php是检查后缀里头有没有ph ,检查文件类型那…

贪心与单调栈的艺术:从三道 LeetCode 题看最小字典序问题(316/402/1081)

前言 欢迎来到我的算法探索博客,在这里,我将通过解析精选的LeetCode题目,与您分享深刻的解题思路、多元化的解决方案以及宝贵的实战经验,旨在帮助每一位读者提升编程技能,领略算法之美。 👉更多高频有趣Lee…

【含开题报告+文档+PPT+源码】基于SpringBoot的校园论坛系统的设计与实现

开题报告 本研究论文主要探讨并实现了一个基于SpringBoot框架构建的全方位校园论坛系统。此系统旨在为校内师生提供一个信息交流与分享的互动平台,核心功能涵盖了校园新闻新闻的实时浏览与更新,用户可自主发布各类主题帖子,并支持深度互动&a…

关于视频字幕

文章目录 视频字幕分类内嵌字幕内封字幕外挂字幕 字幕格式纯文本字幕特效字幕图形字幕 简易修改字幕修改时间同步PotplayerSubtitleEdit 提取蓝光原盘字幕参考资料 视频字幕分类 内嵌字幕 合成到画面的硬字幕,不可移除。 内封字幕 常见的如 MKV 文件&#xff0c…

【AI 语音】实时语音交互优化全解析:从 RTC 技术到双讲处理

网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…

数据结构(栈结构之顺序栈操作实现一)

目录 一.栈结构之顺序栈操作实现 1.项目结构以及初始代码 2.初始化栈结构 3.入栈操作并显示 4.出栈操作并显示出栈元素 5.获取栈长度 6.清空栈 7.销毁栈 8.动态扩展栈空间 一.栈结构之顺序栈操作实现 1.项目结构以及初始代码 SeqStack.h #ifndef __SEQSTACK_H__ #de…

【React】受控组件和非受控组件

目录 受控组件非受控组件基于ref获取DOM元素1、在标签中使用2、在组件中使用 受控组件 表单元素的状态(值)由 React 组件的 state 完全控制。组件的 state 保存了表单元素的值,并且每次用户输入时,React 通过事件处理程序来更新 …

vue2:如何动态控制el-form-item之间的行间距

需求 某页面有查看和编辑两种状态: 编辑: 查看: 可以看到,查看时,行间距太大导致页面不紧凑,所以希望缩小查看是的行间距。 行间距设置 行间距通常是通过 CSS 的 margin 或 padding 属性来控制的。在 Element UI 的样式表中,.el-form-item 的下边距(margin-bottom)…

亚博microros小车-原生ubuntu支持系列:20 ROS Robot APP建图

依赖工程 新建工程laserscan_to_point_publisher src/laserscan_to_point_publisher/laserscan_to_point_publisher/目录下新建文件laserscan_to_point_publish.py #!/usr/bin/env python3import rclpy from rclpy.node import Node from geometry_msgs.msg import PoseStam…