从TCP到RPC:4个分布式通信协议的进化故事(附面试实例+避坑指南)

张开发
2026/4/14 16:29:11 15 分钟阅读

分享文章

从TCP到RPC:4个分布式通信协议的进化故事(附面试实例+避坑指南)
面试必问很多后端同学被问“TCP、HTTP、REST、RPC到底有啥区别”“为什么要有这么多协议”时要么答得颠三倒四要么只懂皮毛说不出底层逻辑——其实这4个协议不是凭空出现的而是一步步“进化”来的就像从“书信”到“微信”每一步都在解决上一代的“痛点”。今天就用“讲故事”的方式把这4个协议的来龙去脉讲透结合面试高频场景和真实实例看完不仅能分清区别还能轻松应对“RPC底层链路拆解”这类难题文末附面试避坑技巧别错过。故事开端没有协议的年代两台机器就是“鸡同鸭讲”在分布式系统还没普及的时候程序员面临一个最基础的问题如何让A机器和B机器“对话”比如你在电脑A机器上写了一段代码想调用另一台服务器B机器上的计算方法这时候两台机器之间只有物理网络连接却没有“共同语言”——A机器发出去的“1010”B机器不知道是“调用方法A”还是“传递参数10”更不知道怎么回应。就像两个人隔着墙喊话没有约定好“喊一声代表同意喊两声代表拒绝”再大声也没用。这时候第一个“约定”——TCP协议应运而生。第一代“通信约定”TCP——解决“可靠对话”的问题为什么会有TCP核心痛点解决“数据传丢、传错”早期的网络传输很“粗糙”数据从A机器发出去可能因为网络波动丢包、乱序比如A发“1、2、3”B可能只收到“1、3”或者收到“2、1、3”。对于需要精准通信的场景比如文件传输、程序调用这种“不靠谱”根本无法接受。于是TCP协议站了出来它的核心使命就是保证数据“可靠、有序、不重复”地从A传到B就像两个人打电话一方说“喂听到了吗”另一方回应“听到了你说”确认无误后再开始聊天聊完还要说“再见”确保对方都听全了。TCP的核心逻辑面试必记TCP不关心你传的是“方法调用”还是“文本文件”它只负责把数据拆成一个个“数据包”给每个数据包编号核心通过“三次握手”建立可靠连接、“四次挥手”安全断开连接中途如果发现丢包就重新发送直到对方确认收到所有数据包。这两个过程是TCP“可靠”的核心也是面试高频考点下面用通俗语言专业逻辑讲透新手也能听懂1. 三次握手建立连接相当于“确认双方都能正常通话”三次握手的核心目的确保客户端和服务端都能正常发送、接收数据避免“一方能发但收不到”的无效连接浪费资源。我们用“打电话”的场景类比再讲专业逻辑通俗类比你客户端给朋友服务端打电话——① 你拨号后朋友电话响起你先开口“喂能听到我说话吗”第一次握手客户端→服务端发送SYN包告诉服务端“我想和你建立连接我的初始序列号是X”② 朋友听到后回应“能听到你能听到我吗”第二次握手服务端→客户端发送SYNACK包告诉客户端“我收到你的请求了我同意建立连接我的初始序列号是Y同时确认收到你的序列号X”③ 你听到朋友的回应再回复“能听到我们开始聊吧”第三次握手客户端→服务端发送ACK包告诉服务端“我收到你的确认了我也确认你的序列号Y连接正式建立可以传数据了”。专业重点三次握手缺一不可。如果只握两次服务端无法确认客户端能收到自己的回应比如客户端发了请求就断网服务端却以为连接建立一直等待数据浪费资源三次握手刚好完成“双向确认”双方都明确对方能发、能收。2. 四次挥手断开连接相当于“确认双方都聊完了安全挂电话”四次挥手的核心目的确保双方都已经把所有数据传输完毕避免断开连接后还有数据未传输导致数据丢失。同样用“打电话”类比再讲专业逻辑通俗类比你和朋友聊完天准备挂电话——① 你说“我说完了挂了啊”第一次挥手客户端→服务端发送FIN包告诉服务端“我这边没有数据要发了准备断开连接”② 朋友回应“好我知道了你等我一下我还有两句话没说”第二次挥手服务端→客户端发送ACK包告诉客户端“我收到你要断开的请求了但我这边还有数据没传完你先别断等我传完”③ 朋友说完最后两句话说“我也说完了挂吧”第三次挥手服务端→客户端发送FIN包告诉客户端“我这边数据也传完了现在可以断开连接了”④ 你回应“好挂了”第四次挥手客户端→服务端发送ACK包告诉服务端“我收到你可以断开的通知了我这边也准备好断开你收到这个确认后我们就正式断连”服务端收到最后一个ACK包后就会关闭连接客户端会等待一段时间确保服务端收到确认再关闭连接避免服务端没收到确认而一直等待。专业重点四次挥手比三次握手多一次核心是“服务端可能还有未传输完的数据”需要分两步回应先确认收到断开请求再完成自身数据传输后发起断开请求而三次握手是“双向确认建立连接”无需额外等待数据。举个通俗例子你给朋友发快递数据TCP就像快递员会把你的包裹数据包编号先打电话问朋友“在家吗能收快递不”三次握手确认在家后再送货送完后还要确认“包裹收到了吗没问题吧”四次挥手如果包裹丢了就重新发一个。TCP的“遗憾”只负责传不关心“传的是什么”TCP解决了“可靠传输”的问题但新的痛点又来了它只负责把数据从A传到B却不规定“数据格式”。比如A机器发“getUser:1001”B机器收到后不知道这是“要查询ID为1001的用户”还是“要删除ID为1001的用户”——因为TCP没有约定“指令格式”。这就像两个人打电话虽然能听到对方的声音可靠传输但一个说中文一个说英文还是无法沟通。于是第二代协议——HTTP协议登场了。第二代“通信约定”HTTP——解决“数据格式统一”的问题为什么会有HTTP核心痛点让“请求和响应”有统一格式20世纪80年代末欧洲核子研究中心的科学家们需要高效共享海量研究文档但传统方式如邮件、FTP存在诸多问题文档分散在不同计算机中访问需手动输入路径格式不统一难以跨平台阅读缺乏动态关联性无法通过链接直接跳转相关内容。1989年蒂姆·伯纳斯-李Tim Berners-Lee博士提出了万维网WWW的构想为了实现这一构想他同时设计了HTTP、HTML和URL——其中HTTP的核心作用就是定义“客户端和服务器之间的请求/响应格式”让双方都能看懂对方的意图。简单说HTTP在TCP的基础上加了一层“规范”规定了请求的“方法”GET查、POST增、PUT改、DELETE删、请求头告诉服务器“我要什么格式的数据”、请求体具体的参数比如“id1001”服务器收到后按照这个规范返回响应状态码响应体。HTTP的核心逻辑面试高频HTTP是“请求-响应”模式基于TCP协议先建立TCP连接再传输HTTP数据它的核心特点是“无状态”——每次请求都是独立的服务器不会记住上一次的请求信息比如你第一次请求“登录”第二次请求“查数据”服务器不会自动关联你的登录状态需要你每次都带“令牌”。举个真实实例你在浏览器输入“www.baidu.com”本质就是浏览器客户端向百度服务器发送一个HTTP GET请求GET / HTTP/1.1 Host: www.baidu.com Accept: text/html服务器收到后返回HTTP响应状态码200表示成功响应体就是百度首页的HTML代码浏览器解析HTML就显示出百度首页。HTTP的“遗憾”太“通用”不够“高效”HTTP解决了“格式统一”的问题成为了互联网的“通用语言”但随着分布式系统的发展它的缺点也越来越明显1. 冗余太多HTTP请求头包含大量无关信息比如Cookie、Cache-Control等即使只传一个简单的参数比如id1也要带一堆请求头传输效率低2. 无状态的弊端每次请求都要重新携带身份信息比如Token增加了数据传输量3. 不适合“内部服务调用”比如微服务中A服务调用B服务的“计算方法”不需要HTML页面只需要传递方法名和参数但HTTP的格式太繁琐效率太低。这时候第三代协议——REST协议对HTTP进行了“优化”。第三代“通信约定”REST——让HTTP更适合“接口调用”为什么会有REST核心痛点规范HTTP接口提升调用效率RESTRepresentational State Transfer表述性状态转移不是一个新协议而是一种“HTTP接口设计规范”——它的核心思想是“用HTTP的方法来表示资源的操作”让接口更简洁、更规范解决HTTP接口“混乱不堪”的问题。在REST出现之前很多开发者写HTTP接口很随意查询用户用“GET /user?id1001”也有人用“POST /getUser”删除用户用“GET /deleteUser?id1001”也有人用“DELETE /user/1001”——没有统一规范导致接口难以维护尤其是在微服务集群中服务之间调用会很混乱。REST的出现就是给HTTP接口“定规矩”让接口更符合“资源导向”比如“用户”是一个资源所有操作都围绕“用户”这个资源展开。REST的核心规范面试必背1. 用HTTP方法表示操作类型GET查询、POST新增、PUT修改、DELETE删除2. 用URL表示资源比如“/users”表示所有用户“/users/1001”表示ID为1001的用户3. 无状态和HTTP一致每次请求都要携带身份信息4. 返回统一格式通常用JSON比HTML更简洁比如查询ID为1001的用户响应体是{id:1001,name:张三,age:25}。REST的真实实例日常开发高频假设我们有一个“用户服务”用REST规范设计接口查询所有用户GET /api/users查询单个用户GET /api/users/1001新增用户POST /api/users请求体{name:张三,age:25}修改用户PUT /api/users/1001请求体{name:李四,age:26}删除用户DELETE /api/users/1001这样的接口无论哪个开发者来看都能一眼看懂用途而且格式简洁传输效率比传统HTTP接口高很多——这也是为什么现在大部分后端接口都是REST风格。REST的“遗憾”还是不够“快”不适合“高频内部调用”REST虽然优化了HTTP接口但它本质还是基于HTTP协议依然存在“请求头冗余、序列化效率低”的问题。比如在大型微服务集群中A服务每秒要调用B服务1000次每次调用都要传递HTTP请求头还要用JSON序列化参数JSON是文本格式序列化/反序列化速度慢长期下来性能损耗会非常大。而且REST接口调用时开发者需要手动拼接URL、设置请求头、处理响应体不够“便捷”——有没有一种方式能让开发者像调用“本地方法”一样调用远程服务于是第四代“通信约定”——RPC协议应运而生成为了分布式系统内部调用的“首选”。第四代“通信约定”RPC——让远程调用像“本地调用”一样简单为什么会有RPC核心痛点解决“内部服务高频调用”的效率和便捷性问题RPCRemote Procedure Call远程过程调用的核心目标就是“屏蔽远程通信的复杂性让开发者调用远程服务就像调用本地方法一样简单”——这也是面试中常考的“RPC核心价值”。比如你在A机器的代码中写了“userService.getUser(1001)”看起来是调用本地方法但实际上这个方法是在B机器上执行的——RPC帮你搞定了“远程通信、数据序列化、服务查找”等所有底层细节你完全不用关心“数据怎么传、服务在哪里”。而且RPC为了提升效率做了两个关键优化① 用二进制序列化比如Protobuf、Hessian替代JSON文本序列化速度更快、体积更小② 精简协议去掉了HTTP中冗余的请求头只传输“方法名、参数、请求ID”等核心信息。RPC的核心链路面试重中之重拆解7个关键环节很多同学面试时被问“RPC调用的底层细节”只会说“用到了动态代理”却讲不清完整链路——其实RPC的调用过程分为7个关键环节结合实例一次性记牢假设场景A机器的客户端调用B机器的“用户服务”获取ID为1001的用户信息。动态代理屏蔽远程通信细节客户端会通过JDK动态代理或CGLIB生成“用户服务”的代理对象比如userServiceProxy。当你调用userServiceProxy.getUser(1001)时代理对象会拦截这个调用把“远程调用”伪装成“本地调用”你完全感知不到远程通信的存在。序列化将请求信息转成二进制客户端会把“方法名getUser、参数1001、请求ID”等信息通过二进制序列化协议比如Protobuf转换成二进制数据流——因为二进制数据传输速度更快更适合网络传输。服务发现找到目标服务地址客户端会通过“服务注册中心”比如Zookeeper、Nacos根据服务名比如“user-service”查询到B机器的IP地址和端口比如192.168.1.100:8080——这一步解决了“不知道服务在哪里”的问题。网络传输发送请求到目标机器客户端通过网络框架比如Netty将二进制数据流通过TCP协议发送到B机器的指定端口——这里的TCP就是我们最开始讲的“可靠传输协议”保证数据不丢包、不乱序。反序列化还原请求信息B机器的服务端会通过Netty监听指定端口收到二进制数据流后进行“反序列化”操作把二进制数据还原成“方法名getUser、参数1001”等原始信息为后续方法调用做准备。执行本地方法生成返回结果服务端根据反序列化得到的信息找到本地的“用户服务”实现类执行getUser(1001)方法查询数据库生成返回结果比如{id:1001,name:张三}。响应返回客户端接收结果服务端将返回结果再次进行二进制序列化通过TCP传输回A机器的客户端客户端接收二进制数据后进行反序列化得到最终的用户信息然后返回给调用者——整个RPC调用完成。RPC的优化机制面试加分项为了保证RPC调用的“可靠性”和“高性能”实际应用中还会加入这些优化1. 可靠性优化请求超时重试、失败降级、心跳检测防止服务宕机未被发现2. 性能优化连接池复用避免频繁建立TCP连接节省资源、批量调用一次传输多个请求减少网络开销、数据压缩缩小二进制数据体积提升传输速度。RPC的真实实例主流框架日常开发中我们很少自己手写RPC框架都是用成熟的框架比如Dubbo阿里开源的RPC框架国内微服务场景应用最广基于TCP协议支持多种序列化方式和服务治理功能gRPCGoogle开源的RPC框架基于HTTP/2协议兼顾了HTTP的通用性和RPC的高效性使用Protobuf序列化跨语言支持性好ThriftFacebook开源的RPC框架序列化效率极高适合高并发、大数据量的场景可选择TCP或UDP作为传输方式。这里要特别纠正一个常见认知RPC的底层并非都是TCP。RPC是“远程过程调用的思想/框架”TCP是“可靠传输协议”二者是“上层框架”与“底层传输”的关系——RPC可以基于TCP也可以基于HTTP/2等其他传输协议甚至部分场景如对可靠性要求不高的日志传输可基于UDP。但为什么大家会觉得“RPC底层都是TCP”核心是TCP的“可靠、有序”特性刚好匹配RPC远程调用的核心需求避免参数、方法名等核心数据丢包因此绝大多数主流RPC框架如Dubbo都会优先选择TCP作为传输方式这也让TCP成为RPC最主流的底层传输协议。Dubbo阿里开源的RPC框架国内微服务场景应用最广基于TCP协议支持多种序列化方式和服务治理功能gRPCGoogle开源的RPC框架基于HTTP/2协议兼顾了HTTP的通用性和RPC的高效性使用Protobuf序列化跨语言支持性好ThriftFacebook开源的RPC框架序列化效率极高适合高并发、大数据量的场景。总结4个协议的进化脉络面试避坑指南一、进化脉络一句话记牢TCP解决可靠传输→ HTTP解决格式统一→ REST规范HTTP接口→ RPC解决内部调用效率和便捷性—— 每一步都是为了解决上一代的痛点适配不同的通信场景。二、核心区别面试必背表格协议核心作用传输方式适用场景TCP保证数据可靠、有序传输二进制流所有需要可靠传输的场景HTTP、RPC都基于TCPHTTP定义请求/响应格式实现通用通信文本/二进制HTTP/1.1文本HTTP/2二进制浏览器访问、对外接口跨语言、跨平台REST规范HTTP接口简洁高效JSON/XML文本前后端分离、对外API、中小规模微服务RPC屏蔽远程调用细节提升效率二进制流大型微服务内部调用、高频调用场景三、面试避坑提醒1. 误区RPC和HTTP是对立的错RPC可以基于TCP也可以基于HTTP比如gRPC基于HTTP/2二者不是对立关系而是适配不同场景——对外用HTTP/REST对内用RPC。2. 误区REST就是HTTP错REST是HTTP接口的设计规范不是新协议没有RESTHTTP接口也能工作只是不够规范。3. 高频考点RPC的核心链路和动态代理的作用——记牢上面的7个环节面试时能流畅拆解就能加分。如果觉得这篇文章对你有帮助记得点赞收藏关注后续会更新更多面试干货和技术拆解助力你轻松拿offer

更多文章