小盟 發表於 2025-11-24 23:39:00

有了TCP为什么还需要HTTP?再用RPC?这次彻底讲明白了

<blockquote>
<p>掘金原文(个人技术文章优先在掘金发布):https://juejin.cn/post/7559981310472470562<br>
记得刚工作那会儿,第一次接触RPC概念时,我内心满是疑惑——明明HTTP用得好好的,为什么要搞出个RPC?直到参与了几个微服务项目后,我才真正能理解它们各自的价值。今天,就让我们一起理清这些协议之间的关系。</p>
</blockquote>
<h2 id="从网络基础说起tcp的能力与局限">从网络基础说起:TCP的能力与局限</h2>
<p>刚开始接触网络编程时,我觉得TCP已经足够完美——它能够建立稳定的连接、保证数据可靠传输、处理网络拥塞,这似乎就是网络通信的全部需求。</p>
<p>但在实际开发中,我遇到了一个基础却关键的问题:</p>
<pre><code class="language-java">// 发送方连续发送两条独立消息
socket.write("Hello");
socket.write("World");

// 接收方可能一次收到:"HelloWorld"
// 完全无法区分原始的消息边界
</code></pre>
<p>这一现象常被称作<strong>TCP粘包问题</strong>,但这其实是一种误解。其<strong>本质是TCP作为字节流协议的无边界特性</strong>:TCP只负责可靠地传输字节序列,却不关心这些字节应该如何被组织成有意义的业务消息。</p>
<p><strong>TCP的三个核心特性:</strong></p>
<ul>
<li><strong>面向连接</strong>:通信前需要通过三次握手建立连接</li>
<li><strong>可靠传输</strong>:通过确认、重传、排序等机制保证数据可靠送达</li>
<li><strong>基于字节流</strong>:数据没有自然边界,只是连续的字节序列(二进制)</li>
</ul>
<p><strong>个人理解:</strong> TCP就像是一个<strong>可靠</strong>的物流系统,保证把所有的货物(字节)都按<strong>顺序</strong>送达,但它会把所有货物都装进一条连续的传送带(字节流)——货物确实都到了,但接收方需要自己根据包裹上的信息(应用层协议)来重新拆分和识别每个独立的包裹。</p>
<blockquote>
<p>所谓“粘包”,其实是一个伪命题。TCP传输的是字节流,而非消息包。如何在流中界定消息,是应用层协议需要解决的问题。</p>
</blockquote>
<h2 id="http协议的出现为数据赋予意义">HTTP协议的出现:为数据赋予意义</h2>
<p>面对TCP的局限性,<strong>应用层协议</strong>应运而生。HTTP协议通过在TCP之上定义明确的报文格式,解决了消息边界和语义表达的问题。</p>
<p><strong>HTTP通过标准的报文结构定义消息边界:</strong></p>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 48

{"name": "Alice", "email": "alice@example.com"}
</code></pre>
<p>关键就在于<code>Content-Length: 48</code>这个头部——它明确告诉接收方消息体的确切长度,从而解决了TCP的粘包问题。</p>
<p><strong>HTTP协议的主要价值:</strong></p>
<ul>
<li><strong>定义消息边界</strong>:通过Content-Length或chunked编码标识消息范围</li>
<li><strong>标准化通信语义</strong>:GET、POST、PUT、DELETE等标准方法</li>
<li><strong>丰富的能力扩展</strong>:缓存控制、内容协商、状态管理</li>
<li><strong>通用兼容性</strong>:被所有主流平台和语言支持</li>
</ul>
<p><strong>结论:</strong> TCP解决了"可靠传输"的问题,而HTTP等应用层协议解决了"传输什么"和"如何解析"的问题。</p>
<h2 id="新的困惑为什么要在http之上再引入rpc">新的困惑:为什么要在HTTP之上再引入RPC?</h2>
<p>随着分布式系统的发展,大家都能发现基于HTTP的服务间调用会存在一些不便:</p>
<pre><code class="language-java">// 基于HTTP的服务调用需要大量样板代码
HttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost("http://user-service/getUser");
post.setHeader("Content-Type", "application/json");

String jsonBody = "{\"user_id\": 123}";
post.setEntity(new StringEntity(jsonBody));

HttpResponse response = client.execute(post);
if (response.getStatusLine().getStatusCode() == 200) {
    String responseBody = EntityUtils.toString(response.getEntity());
    User user = objectMapper.readValue(responseBody, User.class);
}
// 太多的底层细节需要处理!
</code></pre>
<p>每次调用都需要处理HTTP状态码、异常情况、序列化反序列化等重复工作。这让我们开始思考:<strong>能否让远程服务调用像调用本地方法一样简单?</strong></p>
<h2 id="rpc的核心理念透明化的远程调用">RPC的核心理念:透明化的远程调用</h2>
<p><strong>RPC(Remote Procedure Call),又叫做远程过程调用</strong>,其目标就是让远程服务调用对开发者透明。</p>
<pre><code class="language-java">// 理想的RPC调用方式
User user = userService.getUser(123);

// 而不是处理各种网络通信细节
</code></pre>
<p><strong>重要概念澄清(基于个人理解):</strong><br>
RPC本身不是具体的协议,而是一种<strong>调用范式</strong>或<strong>技术思想</strong>。它的核心目标是让程序员能够像调用本地方法那样调用远程服务。</p>
<p><strong>但是我们不能说“使用RPC协议”,因为RPC本身不是协议。</strong></p>
<p><strong>正确的表述方式:</strong></p>
<ul>
<li>✅ "我们使用gRPC协议进行RPC调用"</li>
<li>❌ "我们使用RPC协议"</li>
</ul>
<p>因为RPC本身不是协议,而gRPC、Thrift、Dubbo等才是具体的协议实现。</p>
<h2 id="rpc与http的关系不同维度的技术">RPC与HTTP的关系:不同维度的技术</h2>
<p>RPC与HTTP的关系这是最容易产生混淆的地方。通过学习和实践,我个人理解为:</p>
<p><strong>RPC和HTTP根本不在同一个技术层级:</strong></p>
<ul>
<li>RPC是一种<strong>调用范式</strong>,对标的是<strong>本地方法调用</strong></li>
<li>HTTP是一种具体的<strong>应用层协议</strong></li>
<li>RPC可以通过HTTP实现,也可以通过自定义的TCP或UDP协议实现</li>
</ul>
<p>比如gRPC选择基于HTTP/2协议实现,而很多早期的RPC框架使用自定义的TCP二进制协议。</p>
<h2 id="rpc协议与rpc框架的完整生态">RPC协议与RPC框架的完整生态</h2>
<h3 id="rpc协议通信的基础规范">RPC协议:通信的基础规范</h3>
<p>协议主要定义三个核心方面:</p>
<ol>
<li><strong>消息格式</strong>:定义消息如何开始、结束,头部和体部的结构</li>
<li><strong>序列化方式</strong>:规定数据如何编码和解码</li>
<li><strong>传输规则</strong>:确定通信流程和交互模式</li>
</ol>
<h3 id="rpc框架基于协议的完整解决方案">RPC框架:基于协议的完整解决方案</h3>
<p>现代RPC框架在基础协议之上提供了企业级的能力:</p>
<ul>
<li><strong>核心层</strong>:实现<strong>通信协议</strong>本身</li>
<li><strong>组件层</strong>:提供<strong>序列化、服务发现、负载均衡</strong>等基础组件</li>
<li><strong>治理层</strong>:包含<strong>容错、监控、注册中心</strong>等运维能力</li>
<li><strong>应用层</strong>:让开发者透明地进行远程方法调用</li>
</ul>
<p><strong>总结:</strong> 协议定义了机器之间如何对话,框架让开发者无需关心对话过程而专注业务逻辑。</p>
<h2 id="现代架构中的技术选型各司其职的分工">现代架构中的技术选型:各司其职的分工</h2>
<p>通过参与实际项目,我逐渐理解了HTTP和RPC在现代架构中的分工原则。针对大部分公司而言,基本上都是如此。</p>
<h3 id="内部服务调用rpc框架的优势领域">内部服务调用:RPC框架的优势领域</h3>
<p>在<strong>微服务架构内部</strong>,RPC框架因其性能优势成为首选:</p>
<p><strong>RPC的高性能主要源于:</strong></p>
<ol>
<li>
<p><strong>高效的序列化</strong></p>
<pre><code class="language-java">// JSON序列化:可读性好但体积大
{"id": 123, "name": "Alice", "age": 30}// 约40字节

// Protobuf二进制:体积小,解析快
\x08\x7B\x12\x05Alice\x18\x1E// 仅11字节
// 体积减少70%以上,序列化速度提升明显
</code></pre>
</li>
<li>
<p><strong>协议开销优化</strong></p>
<ul>
<li>长连接减少TCP握手开销</li>
<li>多路复用提升连接利用率</li>
<li>头部压缩减少传输数据量</li>
</ul>
</li>
<li>
<p><strong>专为性能设计</strong></p>
<ul>
<li>精简的协议头设计</li>
<li>二进制编码效率</li>
<li>零拷贝等技术应用</li>
</ul>
</li>
</ol>
<h3 id="对外api暴露http协议的不可替代性">对外API暴露:HTTP协议的不可替代性</h3>
<p>当需要向<strong>外部提供API</strong>时,HTTP协议展现出其独特价值:</p>
<p><strong>HTTP的通用性优势:</strong></p>
<ul>
<li><strong>无与伦比的生态系统</strong>:所有平台、语言、设备都支持HTTP</li>
<li><strong>成熟的工具链</strong>:浏览器、Postman、curl等调试工具</li>
<li><strong>网络友好性</strong>:80/443端口普遍开放</li>
<li><strong>易于测试调试</strong>:直接通过浏览器或命令行工具测试</li>
</ul>
<pre><code class="language-javascript">// 前端调用HTTP API简单直接
fetch('/api/users/123')
.then(response =&gt; response.json())
.then(user =&gt; {
    displayUser(user);
});
</code></pre>
<h2 id="真实世界的架构实践">真实世界的架构实践</h2>
<p>现代互联网公司的典型架构体现了清晰的分层设计:</p>
<p><img src="https://img2024.cnblogs.com/blog/3703499/202511/3703499-20251124233355527-369747172.png"></p>
<p><strong>这种架构的智慧:</strong></p>
<ul>
<li><strong>内外有别</strong>:对外保证兼容性,对内追求性能</li>
<li><strong>关注点分离</strong>:网关处理横切关注点,业务服务专注核心逻辑</li>
<li><strong>协议转换</strong>:在网关层完成HTTP到RPC的协议转换</li>
</ul>
<h2 id="总结与核心观点">总结与核心观点</h2>
<p>针对文章内容,整体总结一下:</p>
<p><strong>技术演进脉络:</strong></p>
<ol>
<li><strong>TCP层</strong>:解决网络层的可靠传输问题</li>
<li><strong>HTTP层</strong>:解决应用层的消息格式和语义问题</li>
<li><strong>RPC层</strong>:优化分布式系统的服务调用体验</li>
</ol>
<p><strong>现代架构的最佳实践:</strong></p>
<ul>
<li>
<p><strong>公司内部服务调用</strong>:优先采用RPC框架(如gRPC、Dubbo)</p>
<ul>
<li>核心原因:极致性能。二进制编码、协议开销低、长连接复用</li>
</ul>
</li>
<li>
<p><strong>对外暴露接口</strong>:普遍采用HTTP协议(及RESTful风格)</p>
<ul>
<li>核心原因:无与伦比的通用性。生态系统成熟,客户端兼容性好</li>
</ul>
</li>
</ul>
<p><strong>核心结论:</strong><br>
这是一个"内外有别"的最佳实践——性能至上的内部集群采用RPC框架,兼容性优先的对外开放接口采用HTTP协议。</p>
<blockquote>
<p>本文内容是作者在学习过程中的个人理解和总结,可能存在不准确或理解有误的地方,欢迎倔友们指正。</p>
<p>当然我也已经练习长达两年半了😄,理论上🤌应该没问题。<br>
<img src="https://img2024.cnblogs.com/blog/3703499/202511/3703499-20251124233417535-1141321522.png"></p>
</blockquote>
<hr>
<blockquote>
<p>文章的最后,想和你多聊两句。</p>
<p>技术之路,常常是热闹与孤独并存。那些深夜的调试、灵光一闪的方案、还有踩坑爬起后的顿悟,如果能有人一起聊聊,该多好。</p>
<p>为此,我建了一个小花园——我的微信公众号「<strong>[努力的小郑]</strong>」。</p>
<p>这里没有高深莫测的理论堆砌,只有我对后端开发、系统设计和工程实践的持续思考与沉淀。它更像我的<strong>数字笔记本</strong>,记录着那些值得被记住的解决方案和思维火花。</p>
<p>如果你觉得今天的文章还有一点启发,或者单纯想找一个同行者偶尔聊聊技术、谈谈思考,那么,欢迎你来坐坐。<br>
<img src="https://img2024.cnblogs.com/blog/3703499/202601/3703499-20260105210259813-964799315.jpg"></p>
<p>愿你前行路上,总有代码可写,有梦可追,也有灯火可亲。</p>
</blockquote><br><br>
来源:https://www.cnblogs.com/xzqcsj/p/19266220
頁: [1]
查看完整版本: 有了TCP为什么还需要HTTP?再用RPC?这次彻底讲明白了