计算机网络相关 讲一下rpc与传统http的区别

这是一个非常硬核且经典的问题。要真正理解 RPC(Remote Procedure Call,远程过程调用)和 HTTP 的区别,以及如何手写一个 RPC 框架,我们需要深入操作系统的网络层、IO 模型以及序列化协议。

第一部分:RPC 与 传统 HTTP 的区别

1. 概念上的误区

首先要纠正一个常见的误区:RPC 和 HTTP 不是对立的

  • RPC是一种设计思想(我要像调用本地函数一样调用远程函数)。
  • HTTP是一个传输协议

事实上,很多 RPC 框架(如 gRPC)底层用的就是 HTTP/2。我们通常所说的 "RPC vs HTTP",实际上是在比较"RPC 风格(如 gRPC, Dubbo)""RESTful 风格(基于 HTTP/1.1 + JSON)"

2. 核心区别对比

特性

RPC (以 gRPC/Dubbo 为例)

RESTful HTTP (传统 Web API)

设计初衷

动作导向。隐藏网络细节,让远程调用像本地调用一样(service.getUser(id))。

资源导向。操作资源的状态(GET /users/1)。

传输协议

通常基于TCPHTTP/2

通常基于HTTP/1.1

报文体积

极小。通常使用二进制序列化(Protobuf, Thrift, Hessian),没有冗余 Header。

较大。使用文本(JSON/XML),且 HTTP 1.1 Header 通常包含大量元数据。

性能

。基于二进制、TCP 长连接、多路复用。

中等。JSON 解析慢,HTTP/1.1 存在队头阻塞问题。

开发体验

强类型。通常需要 IDL 文件(如.proto)生成代码,客户端服务端强契约。

弱类型。接口灵活,通常看文档对接,容易出现字段拼写错误。

适用场景

微服务内部通信(追求低延迟、高吞吐)。

对外接口(如移动端、Web 前端),因为 HTTP 通用性最好。

总结:

  • HTTP (REST)像寄信:格式通用,谁都能读,但信封(Header)很厚,且一来一回慢。
  • RPC (TCP/Binary)像专线电话:语言精简(二进制),双方约定暗号(IDL),连接建立后说话极快。

第二部分:涉及的计算机网络硬核知识

要手搓 RPC,你必须解决网络通信中的三大核心问题。这也是计算机网络的精髓。

1. 寻址与传输(Layer 4 - Transport Layer)
  • Socket 编程:RPC 的本质是网络通信。你需要使用 Socket API(在 Java 中是Socket/ServerSocket,在 Go 中是net.Dial/net.Listen)。
  • TCP 连接复用:建立 TCP 连接(三次握手)很慢。成熟的 RPC 框架都会使用连接池长连接,避免每次调用都握手。
2. 序列化与反序列化(Layer 6 - Presentation Layer)

网络只能传输 0 和 1(字节流),不能传输内存中的对象(如 Java 的 Object 或 Go 的 Struct)。

  • 序列化(Marshaling):把内存对象变成二进制串。
  • 反序列化(Unmarshaling):把二进制串变回内存对象。
  • 手搓选择:为了简单,我们可以用 JSON。为了性能,通常用 Protobuf 或 Hessian。
3. 拆包与粘包(TCP 字节流特性)

这是很多初学者最容易忽略的点。
TCP 是面向字节流的协议,它没有"消息"的概念。

  • 粘包:你发送了两个请求 "ABC" 和 "DEF",TCP 为了效率可能会把它们合并成 "ABCDEF" 发送过去。
  • 拆包:你发送了一个很大的包,TCP 可能会把它拆成两段发送。

解决方案(自定义应用层协议)
我们需要定义一个协议格式。最常用的方式是Length-Prefix(长度前缀法)
格式:[消息长度 (4字节)] + [消息体 (N字节)]
读取时,先读4个字节拿到长度 N,再读取 N 个字节,这样才能保证读到的是一个完整的 RPC 请求。


第三部分:如何手搓一个简单的 RPC

我们以Java为例(因为 Java 的动态代理最适合演示 RPC 原理),逻辑通用于所有语言。

架构图
[Client App] --调用--> [Client Stub (Proxy)] --序列化--> [Network] | [Server Impl] <--反射调用-- [Server Skeleton] <--反序列化--|
步骤 1: 定义公共接口

客户端和服务端都需要这个接口。

public interface UserService { User getUser(Integer id); }
步骤 2: 客户端代理 (The Magic)

客户端并没有UserService的实现类,怎么调用?用动态代理
代理的作用是:拦截方法调用,把方法名、参数打包发给服务端。

// 这是一个极简的动态代理逻辑 public class RpcClientProxy implements InvocationHandler { private String host; private int port; public RpcClientProxy(String host, int port) { this.host = host; this.port = port; } // 当你调用 userService.getUser(1) 时,会被这个 invoke 方法拦截 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1. 封装请求对象 (包含接口名、方法名、参数类型、参数值) RpcRequest request = new RpcRequest(); request.setClassName(method.getDeclaringClass().getName()); request.setMethodName(method.getName()); request.setParamTypes(method.getParameterTypes()); request.setArgs(args); // 2. 建立网络连接 (Socket) Socket socket = new Socket(host, port); // 3. 发送请求 (序列化) - 这里偷懒用了 Java 自带的序列化,实际应用应换成 JSON/Protobuf ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeObject(request); // 发送数据 // 4. 接收响应 (阻塞等待) ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); Object result = input.readObject(); // 获取结果 // 5. 清理资源 socket.close(); // 6. 返回结果给调用者 return result; } // 获取代理对象的工具方法 public <T> T getProxy(Class<T> interfaceClass) { return (T) Proxy.newProxyInstance( interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass}, this ); } }
步骤 3: 服务端监听 (The Server)

服务端需要在一个端口一直监听,接收请求,找到对应的实现类,执行代码。

public class RpcServer { // 存储接口和服务实现类的映射关系 private Map<String, Object> serviceRegistry = new HashMap<>(); public void register(Class interfaceClass, Object impl) { serviceRegistry.put(interfaceClass.getName(), impl); } public void start(int port) throws Exception { ServerSocket serverSocket = new ServerSocket(port); System.out.println("RPC Server started on port " + port); while (true) { // 1. 监听连接 (BIO 模型,阻塞) Socket socket = serverSocket.accept(); // 2. 开启线程处理 (为了支持并发,实际应该用线程池) new Thread(() -> { try { ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); // 3. 读取请求 (反序列化) RpcRequest request = (RpcRequest) input.readObject(); // 4. 从注册表中找到具体的实现类 Object serviceImpl = serviceRegistry.get(request.getClassName()); // 5. 通过反射调用真实方法 Method method = serviceImpl.getClass().getMethod( request.getMethodName(), request.getParamTypes() ); Object result = method.invoke(serviceImpl, request.getArgs()); // 6. 写回结果 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeObject(result); } catch (Exception e) { e.printStackTrace(); } }).start(); } } }
步骤 4: 跑起来

服务端代码:

UserService service = new UserServiceImpl(); // 真实的业务逻辑 RpcServer server = new RpcServer(); server.register(UserService.class, service); server.start(8080);

客户端代码:

RpcClientProxy proxy = new RpcClientProxy("localhost", 8080); UserService userService = proxy.getProxy(UserService.class); // 这一步看起来像本地调用,实际上走了网络! User user = userService.getUser(1001); System.out.println(user.getName());

第四部分:进阶思考(从玩具到生产级框架)

上面的代码只是一个玩具(Toy RPC),要变成一个生产级框架(如 Dubbo/gRPC),还需要解决以下问题,这也体现了计算机网络的高级应用:

  1. IO 模型(BIO vs NIO)
    • 上面的代码使用了ServerSocket.accept()Stream.read(),这是同步阻塞 IO (BIO)。如果不开启多线程,一个客户端卡住,整个服务端就挂了。
    • 优化:使用Java NIO (Netty)或 Go 的 Goroutine。使用IO 多路复用 (epoll)技术,让一个线程管理成千上万个连接。
  1. 序列化性能
    • Java 原生序列化(ObjectOutputStream)非常慢且生成的数据包很大。
    • 优化:使用Protobuf, Kryo, Hessian。它们基于二进制位操作,体积可能只有 Java 原生的 1/10。
  1. 服务发现(Service Discovery)
    • 上面代码写死了localhost:8080。如果是集群呢?
    • 优化:引入Zookeeper/Nacos/Etcd。服务端启动时把 IP 注册上去,客户端调用时先去注册中心拉取 IP 列表,然后进行负载均衡
  1. 自定义协议(解决粘包)
    • 上面的代码依赖 Java 对象的边界。如果跨语言(Java 调 Go),必须定义字节级协议。
    • 优化:定义Header(包含 Magic Number, Version, Length) +Body

总结

RPC 的本质就是"透明化网络通信"

  • 网络层:利用 TCP/Socket 传输数据。
  • 表示层:利用序列化解决对象传输问题。
  • 应用层:利用动态代理(Stub)让调用者无感知。

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

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

相关文章

OpenCode Skills 使用指南

本文档介绍如何在 OpenCode 中使用 Agent Skills 扩展 AI 编程助手的能力。 目录 什么是 Skills安装 Skills使用 Skills注意事项常见问题相关资源 什么是 Skills Skills 是可重用的 AI Agent 能力扩展&#xff0c;通过 SKILL.md 文件定义&#xff0c;包含 YAML frontmatter&…

如何搜索硕士论文:实用技巧与高效方法指南

刚开始做科研的时候&#xff0c;我一直以为&#xff1a; 文献检索就是在知网、Google Scholar 里反复换关键词。 直到后来才意识到&#xff0c;真正消耗精力的不是“搜不到”&#xff0c;而是—— 你根本不知道最近这个领域发生了什么。 生成式 AI 出现之后&#xff0c;学术检…

如何录制高品质音效素材?2026指南+10个免费素材站推荐

根据《2025-2030年中国音效素材行业市场全景评估及投资战略咨询报告》显示&#xff0c;随着短视频、直播、影视等领域的爆发式增长&#xff0c;高品质音效素材的需求持续上升&#xff0c;越来越多创作者选择自主录制音效以实现个性化表达。那么&#xff0c;怎样才能产出专业级的…

纯 Node.js 编译 LaTeX:无需 TeX Live、无需宏包管理的工程级方案(node-latex-compiler)

&#x1f680; 纯 Node.js 编译 LaTeX&#xff1a;无需 TeX Live、无需宏包管理的工程级方案&#xff08;node-latex-compiler&#xff09; 告别 TeX Live / MiKTeX / 宏包地狱&#xff0c;在 Node 环境下一行代码完成 LaTeX → PDF。 如果你曾尝试在 Node / Electron / CI / D…

Dapr (分布式应用运行时) 入门:不改代码实现“服务调用重试”与“分布式追踪”,Sidecar 模式的终极形态

摘要: 在微服务架构演进的十年间&#xff0c;无论是 Spring Cloud 还是 Istio&#xff0c;都在不断探索如何降低业务代码与基础设施的耦合。微软开源的 Dapr (Distributed Application Runtime) 则给出了“Sidecar 模式”的终极答案&#xff1a;将状态管理、发布订阅、服务调用…

常见影视转场音效素材下载网站有哪些?(2026年1月盘点)

根据《2025年中国数字创意产业发展报告》显示&#xff0c;2025年我国数字创意产业规模突破6万亿元&#xff0c;其中影视制作领域对音效素材的需求同比增长35%&#xff0c;尤其是影视转场音效素材&#xff0c;成为视频内容提升节奏感和观赏性的关键元素。就像做菜需要调料一样&a…

学长亲荐2026TOP10AI论文软件:本科生毕业论文写作全测评

学长亲荐2026TOP10AI论文软件&#xff1a;本科生毕业论文写作全测评 2026年AI论文写作工具测评&#xff1a;为什么你需要这份榜单&#xff1f; 随着人工智能技术的不断成熟&#xff0c;AI写作工具逐渐成为高校学生撰写毕业论文的重要辅助工具。然而&#xff0c;面对市场上琳琅…

Node.js 已死?Bun 1.2 深度评测:HTTP 吞吐量是 Node 的 3 倍,兼容性到底如何?

摘要: 2024 年&#xff0c;前端运行时领域最大的变量莫过于 Bun 1.2 的发布。作为“Node.js 杀手”&#xff0c;Bun 号称 HTTP 吞吐量是 Node 的 3 倍&#xff0c;启动速度快 4 倍。但在生产环境中&#xff0c;标榜的性能数据能否兑现&#xff1f;号称的 “Drop-in Replacement…

Excel效率神器:巧用ISFORMULA与ISREF函数实现智能统计

还在为Excel表格中混合了公式和数值的数据汇总而头疼吗&#xff1f;两个函数一个技巧&#xff0c;教你实现智能数据识别与统计&#xff01; 一、两个关键函数&#xff1a;数据类型的“火眼金睛” 1. ISFORMULA函数 - 公式检测器 ISFORMULA(单元格引用) 功能&#xff1a;判断指…

Fortra GoAnywhere MFT 关键反序列化漏洞分析工具

Fortra GoAnywhere MFT CVE-2025-10035 漏洞分析工具 项目概述 本项目是针对Fortra GoAnywhere MFT中CVE-2025-10035漏洞的分析与利用工具。该漏洞存在于License Servlet组件中&#xff0c;由于不安全的Java对象反序列化机制&#xff0c;攻击者可以通过提交带有有效签名的伪造许…

搜索研究文献的方式探讨:高效获取学术资源的方法与技巧

刚开始做科研的时候&#xff0c;我一直以为&#xff1a; 文献检索就是在知网、Google Scholar 里反复换关键词。 直到后来才意识到&#xff0c;真正消耗精力的不是“搜不到”&#xff0c;而是—— 你根本不知道最近这个领域发生了什么。 生成式 AI 出现之后&#xff0c;学术检…

区块链游戏外包的流程

区块链游戏的外包开发流程相较于传统游戏&#xff0c;更强调经济模型审计、合规性审查和交付物所有权&#xff08;私钥/代码控制权&#xff09;。 以下是一个标准的区块链游戏外包协作流程&#xff1a; 1. 需求分析与 RFP&#xff08;需求建议书&#xff09;阶段 在接触外包…

2024年深圳中学自招真题 (答案版)

2024年深圳中学自招真题 (答案版)2024年深圳中学自招真题 全卷共15题,满分70分 1.(4分)\(\dfrac{630^{2024}+30^{2025}}{30^{2024}-1030^{2023}} =\)____. 【答案】\(54\) 【解答】原式\(=\dfrac{30^{2023} (630+…

springboot_ssm860抑郁症科普交流网站

目录具体实现截图抑郁症科普交流网站摘要系统所用技术介绍写作提纲源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;具体实现截图 抑郁症科普交流网站摘要 抑郁症科普交流网站基于SpringBoot和SSM框架开发&#xff0c;旨在为公众提供科…

工信认证人才培训机构哪家好,推荐广东省空间计算科技集团

(涵盖工信认证人才培训、工业数字化转型、数据资产变现等核心服务领域服务商推荐) 2026年数字化转型浪潮席卷各行各业,工信认证人才培训已成为企业补齐数字化人才短板、突破转型瓶颈的核心抓手。无论是权威认证加持…

艾体宝洞察 | 缓存策略深度解析:从内存缓存到 Redis 分布式缓存

摘要 本文从实际业务需求出发&#xff0c;深入分析了进程内缓存和 Redis 分布式缓存两种主流方案的特点与应用场景。进程内缓存以其极速的访问性能适合单实例应用的轻量级需求&#xff0c;而 Redis 分布式缓存则凭借其强大的功能特性和扩展能力&#xff0c;成为大规模分布式系…

推荐减震隔声垫厂家,如何选择合适的

随着绿色建筑与居住品质需求的提升,电子交联复合保温隔声垫、减震隔声垫等建材逐渐成为建筑工程的刚需,不少企业和项目方都在寻找靠谱的生产商与供应商。本文整理了关于隔声垫采购的高频问题,结合江苏博康特建材有限…

抛弃 ElasticSearch?StarRocks 存算分离实战:亿级日志检索性能压测与成本对比

摘要&#xff1a;在日志分析与检索领域&#xff0c;ElasticSearch (ES) 曾是无可撼动的霸主。但在数据量爆炸的今天&#xff0c;ES 的高存储成本和 JVM 调优噩梦让无数架构师头秃。本文将实战演示如何引入 StarRocks 存算分离架构&#xff0c;在亿级日志场景下&#xff0c;实现…

派对船优质厂家怎么选择?

问题1:什么是个性派对船?和普通游艇有什么区别? 个性派对船是专为海上社交场景设计的定制化船艇,核心是通过灵活的空间布局、多元的娱乐设施和个性化配置,满足不同主题派对(如生日宴、订婚宴、企业团建)的专属需…

将PyTorch/TensorFlow模型迁移到MindSpore

PyTorch/TensorFlow模型迁移到MindSpore的完整指南 一、迁移前的关键决策 1.1 选择迁移策略 **迁移策略矩阵**&#xff1a;| 策略 | 适用场景 | 优点 | 缺点 | |------|----------|------|------| | **算子级重写** | 小规模模型、自定义算子多 | 性能最优&#xff0c;完全可…