最近网上有个说法挺有意思:“我们的系统检测到您的计算机网络中存在异常流量,请稍后重新发送请求。”这句提示一出来,好多人第一反应是拔网线、重启光猫、怀疑IP被抢。作为一个常年写服务端、也常被网关拦过的人,我想说:这条提示跟你的物理网络基本没关系,它发生在应用层,更准确地说,发生在HTTP这一层。服务器收到请求后,在进入业务代码之前,先用头信息、请求频率和行为特征做了一次“安检”,觉得你不像真人,就把你挡在了门外。
计算机网络的教材会把应用层放在整个协议栈的最高层,但教材不会告诉你:这一层才是用户唯一“摸得到”的层。你打开网页、刷视频、发邮件、扫码支付,背后全是应用层协议在对话。这篇文章不准备按教材目录平铺直叙,我想站在应用层这个“业务入口”的角度,把它的职责边界、核心协议、经典事故、数据完整性校验方式,以及期末、408考研和面试里的高频考点串起来。不管你是正在复习还是搞应用层开发,应该都能找到能直接拿去用的东西。
1. 应用层的职责到底划在哪:从一次“异常流量”提示说起
1.1 那个让全网困惑的报错,本质是应用层在“把关”
“我们的系统检测到您的计算机网络中存在异常流量。请稍后重新发送请求。”这句话出现在浏览器页面上的时候,很多人以为是自己家网络出了问题,甚至怀疑路由器被入侵。实际上,这条提示是服务端的风控系统在HTTP这一层发出的“软拒绝”。请求确实通过网络送到了服务器,但服务器在执行业务逻辑之前,先检查了请求的元数据:User-Agent像不像正常浏览器、请求头里有没有合法的Cookie、同一个IP在一个时间窗口内发起了多少次请求、TLS指纹是否符合预期。只要一项可疑,就会直接返回提示页,根本不会让请求进入真正的业务代码。
这个场景非常好地说明了一件事:应用层协议从来不只是“传输数据的格式”,它还承载了服务器用来识别“你是谁、你是什么身份、你打算干什么”的全部信息。HTTP请求的行、头、体,DNS报文里的查询名,DHCP里的选项字段,本质上都是两个程序之间约定的“黑话”,双方都按同一个语义表来理解对方的意思。这就是应用层最核心的职责:定义进程与进程之间的通信语义。
1.2 分层不是比赛谁更厉害,而是一场接力
要理解应用层,躲不开“分层”这个大前提。整个网络协议栈像一条协作流水线:物理层负责把比特变成电信号、光信号,链路层负责同一网段内的成帧与介质访问,网络层负责跨网络的寻址与路由,传输层负责端到端的连接与数据流,而应用层负责“解释数据”。每一层只做自己的事情,然后把结果交给相邻层。
这个设计最重要的价值是解耦。TCP不需要知道你在传输的是网页还是视频,IP也不需要关心目标程序在等什么数据。所以应用层协议才能百花齐放:HTTP、DNS、DHCP、FTP、SMTP、MQTT、WebSocket,它们都构建在同一个传输层之上,却服务完全不同的业务场景。没有分层,任何一个新协议出现,都可能要重写底层网络,那互联网根本不可能发展到今天这个规模。
初学者常见的误区是觉得应用层“太简单了”,不就是几个协议吗。实际上,对绝大多数开发岗位来说,应用层是你唯一每天都要直接打交道的层。写接口、调第三方服务、处理消息队列,本质上都是在做应用层的“语义协商”。而TCP重传、IP分片这些底层机制,你可能几年都碰不到一次,但它们会通过“连接超时”“数据包乱序”这些方式,在你的应用层代码里制造各种玄学故障。
1.3 Socket 是应用层和传输层的“挂号窗口”
聊应用层时一定会被问到Socket,这里我多说一句。Socket不是协议,也不是应用层本身,它是应用层程序调用传输层服务的一组API。你可以把Socket理解成“挂号窗口”:应用进程想用TCP或UDP收发数据,就必须到这个窗口办手续,告诉内核“我要连哪个IP、哪个端口、用TCP还是UDP”,然后内核把网络栈的能力暴露给你。
这也是应用层和传输层之间的边界感:应用层负责构造“语义正确的数据”,传输层负责保证“数据能按顺序、无差错地到达对方进程”。所以Socket编程里,你通常只关心send和recv,TCP的滑动窗口、拥塞控制、重传机制都在内核里偷偷替你处理了。但这里有个陷阱:应用层如果把业务做得过于依赖底层传输保证,一旦网络抖动,底层会不断重传,应用层如果不知道,还傻等响应,就会出现“请求超时了但其实服务端早就收到了”这类经典问题。
2. 三个天天在跑却总被忽略的协议:HTTP、DNS、DHCP
2.1 HTTP:无状态协议如何支撑起“有状态”的现代Web
HTTP是应用层里出场率最高的协议,但它有个非常反直觉的设计:默认无状态。也就是说,服务器收到两次HTTP请求,并不知道它们是不是同一个人发出来的。这跟早期Web文档交换的场景匹配——浏览器向服务器要一个HTML文档,服务器发完就忘,关不关心你是谁并不重要。
现代Web需要登录、购物车、个性化推荐,全都依赖状态。于是一套补丁方案出现了:Cookie 和 Session。服务器在用户登录后,把一份会话标识写入浏览器 Cookie,浏览器每次请求都自动带上这个标识,服务器看到标识就知道“哦,是你”。注意分工:Cookie 存在客户端,Session 存在服务端,两者配合,硬是在无状态的HTTP上搭出了有状态的业务系统。理解这个演进过程很重要,因为面试官最爱问“Cookie 和 Session 有什么区别”,如果你只背结论,不理解“为什么需要状态”,一深挖就露馅。
另一个值得关注的是HTTP版本的演进。HTTP/1.1引入了持久连接,但多个请求在同一个TCP连接里还是得排队,慢的那一个会堵住后面所有请求,这就是队头阻塞。HTTP/2用多路复用解决了一部分问题,真正的破局是HTTP/3,它干脆把底层从TCP换成了基于UDP的QUIC,在用户态实现了可靠传输和快速握手。有人会问:HTTP不是应用层协议吗,怎么还管起传输层了?这正是现代协议栈的一个趋势:应用层不能只满足于定义语义,还得亲自下场解决性能问题,否则底层那个通用协议没法为你做一个业务专属的优化。
2.2 DNS:电话簿、层级查询与缓存
没有DNS,互联网就是一堆IP地址的大杂烩。DNS的核心工作就是把“www.example.com”翻译成“93.184.216.34”,但它不是查一张大表那么简单,而是一个分布式的层级查询系统。根域名服务器知道顶级域服务器在哪,顶级域服务器知道权威服务器在哪,权威服务器才真正记录着某个域名和IP的映射关系。
整个查询过程分两种角色:递归查询和迭代查询。你的电脑问本地递归服务器“www.example.com 的IP是多少”,这是一个递归请求;本地递归服务器替你去问根服务器、顶级域服务器、权威服务器,每一步都是迭代。理解这个链条有个很实际的作用:当你在应用层遇到“域名解析慢”“DNS劫持”“缓存污染”的时候,你能快速定位问题到底在本地缓存、运营商递归服务器,还是权威DNS。线上排查时,我第一件事就是dig +trace看链路,而不是瞎猜。
缓存是DNS性能的关键,也是故障的来源。每个DNS记录都有TTL,也就是“允许缓存多久”。TTL太短,会导致解析压力大;TTL太长,域名切换IP后旧地址迟迟不失效。生产环境改域名解析前,先把TTL调低,等切换完成再调回去,这是老运维都知道的套路,背后就是应用层协议参数和业务连续性的权衡。
2.3 DHCP:全自动的“入职办理”流程
DHCP解决的是一个很接地气的问题:设备接入网络时,怎么自动拿到IP地址、子网掩码、网关和DNS?你要是自己手动配过静态IP,就知道这种东西一多必出错。DHCP的实现思路非常“应用层”:用UDP广播,靠四个报文完成配置——Discover(发现)、Offer(提供)、Request(请求)、Ack(确认)。
设备开机后先广播“我来了,谁有地址可分给我”,DHCP服务器回应“我这有一个租约,你要不要”,设备确认“我要”,服务器批复“成交”。注意“租约”这个词,IP地址是临时分配的,跟租房一样。租期过了要续租,通常到50%和87.5%的时候,设备会主动尝试续约。这也是为什么你偶尔会发现设备IP变了——租约到期后没续上,被服务器分配了另一个地址。
很多应用层联调问题其实出在DHCP上:设备拿到的IP不对、网关不对、DNS不对,应用层表现都是“连不上服务器”。排查思路是先看网卡有没有拿到合法IP,Windows上如果地址是169.254.x.x,说明DHCP失败,设备进入了自分配的保留地址段。这个排查经验本身不值钱,值钱的是理解:应用层依赖的“网络可达性”,栈了多大一层层基础服务的忙。
我手头正好能补一张三者对比表,方便横向理解:
| 协议 | 默认端口 | 传输层 | 核心机制 | 常见故障点 |
|---|---|---|---|---|
| HTTP | 80 / 443 | TCP | 请求-响应、无状态 + Cookie/Session | 队头阻塞、Cookie 丢失、状态失真 |
| DNS | 53 | UDP/TCP | 层级查询 + 缓存 | 递归超时、缓存污染、TTL过长 |
| DHCP | 67 / 68 | UDP | 广播四步 + 租约机制 | 拿不到地址、租约续期失败 |
3. “应用层整车CAN线进入bus-off”:应用层设计失误是怎么拖垮底层链路的
3.1 Bus-off 不是网络安全概念,是总线的“强制下线”
“应用层整车CAN线进入bus-off”这个话题能上热搜,说明汽车电子行业的人对它深有感触。很多人第一次看到bus-off这个词,还以为是“总线离线”之类的网络安全事件,其实它是CAN总线里一个非常具体的错误处理机制。
CAN总线上的每个节点都有两个错误计数器:发送错误计数器TEC和接收错误计数器REC。节点在通信过程中如果发现错误,就要发送错误帧,同时自己的计数器会按规则累加。计数器越大,代表这个节点最近“犯错”越多。当发送错误计数超过一定阈值时,节点会进入bus-off状态——控制器主动断开与总线的连接,不再参与任何通信。这个设计本意是保护总线:一个连续出错的节点如果不隔离,会不断往总线上发送错误帧,把所有正常通信都干扰掉。但从业务角度看,一个节点bus-off,就意味着它对应的控制器彻底掉线了,轻则某个传感器数据缺失,重则整车动力系统出问题。
3.2 应用层调度如何影响总线健康
那为什么说“应用层整车CAN线进入bus-off”,问题可能出在应用层?因为总线上的报文调度、发送周期、错误恢复策略,很大程度是由上层应用(比如整车控制器里的应用软件)决定的。
常见的几个应用层“重灾区”:第一,发送频率设计不合理,总线负载率过高,导致连续位错误;第二,应用层在检测到错误后没有做退避,反而以更高频率重发报文,让错误计数器一路飙升;第三,错误处理逻辑没有和底层驱动对齐,应用层以为报文发送成功了,实际上控制器已经处于bus-off边缘。很多整车厂踩过这个坑之后,总结出的经验是:应用层不但要“把报文发出去”,还要监控总线状态、错误计数和节点状态,一旦出现异常,主动降低发送速率,而不是傻乎乎地继续重试。
我见过最典型的案例是:一个信号在应用层被配置成每10毫秒发一次,但实际总线上同一报文的其他信号也在变,导致仲裁延迟、位填充错误概率上升,最终节点莫名其妙进入bus-off。最后排查出结果,问题根本不在物理层,而是上层调度策略没有给总线留出足够余量。这个案例放到互联网里也是同一个道理:应用层的并发策略、重试策略,如果不考虑底层传输的承受能力,很容易制造连锁故障。
3.3 互联网里的同款故事:重传风暴与连接断开
互联网虽然不叫bus-off,但也有类似的“强制下线”机制。TCP在连续重传超过一定次数后,会放弃连接,通知应用程序“链路不可用”;主流云负载均衡在探测到后端连续超时后,也会把后端节点摘掉流量。这些都是底层的自我保护,但触发它们的往往是应用层行为。
比如说某个服务在数据库变慢之后,请求超时时间设得过长,导致大量请求堆积在服务器上;服务器撑不住了,健康检查开始失败,负载均衡把节点摘掉;剩余节点压力更大,全线崩掉。从应用层开发者视角看,这是“数据库慢查询问题”,但从网络协议栈的视角看,这是应用层没有做超时、熔断、限流,把压力一路传导到了传输层、链路层,最终触发了底层保护机制。
所以,不管是汽车里的bus-off,还是互联网里的连接重置,核心教训都是相通的:做应用层设计时,永远不要把底层传输能力当无限资源。要在应用层设计好背压机制,给请求设定合理的超时,给重试加指数退避,这样才能避免让一个本可局部化的小故障,演变成全网级事故。
4. 数据完整性:CRC 校验、校验和与应用层自己的“验货”机制
4.1 CRC 到底怎么算,为什么能发现错误
热搜词里有“计算机网络中的CRC校验如何通过报文观察”,这确实是个好问题,因为很多教材讲CRC只讲算法,不讲它到底出现在报文的哪个位置。先说结论:CRC校验常见于链路层和CAN总线等通信协议里,以太网帧的尾部FCS字段、CAN帧里的CRC字段,用的都是CRC算法。HTTP、DNS这些应用层协议,反而不依赖CRC,因为TCP/UDP已经做了自己的完整性校验。
CRC的原理可以概括成“用二进制除法代替逐位纠错”。发送方把数据看作一个很长的二进制数,按照事先约定的生成多项式做“模2除法”,得到一个余数,叫CRC校验值,附在数据后面一起发出去。接收方用同样的多项式对“数据加校验值”再做一次除法,如果余数为0,就认为数据没出错。模2除法和普通除法的区别是,它全程用异或运算,不产生借位,非常适合硬件实现。
举个极简例子:假设发送数据是1101,约定生成多项式是G(x) = x^3 + 1,对应二进制1001。发送方先在被除数据后面补3位0,变成1101000,用1001做模2除法,余数为001,把余数拼到原数据后面,实际发送1101001。接收方对1101001再做模2除法,余数为0则通过。这个例子里的数字很小,但原理和真实CRC-32完全一样。
4.2 从报文里怎么看 CRC 和校验和字段
“通过报文观察CRC”这件事,用抓包工具最直观。以太网数据包抓下来,尾部那4个字节就是CRC-32校验值,抓包软件通常会标记为Frame Check Sequence。如果拿到的是解析后的数据,可以看到网卡硬件已经做过校验,错的帧就直接丢了,应用层根本感知不到。所以从应用层抓包,很少看到CRC错误——真正的错误都发生在比应用层更靠下的位置。
TCP和UDP头里其实也有校验字段,叫Checksum,算法和CRC不同,是“反码求和”。TCP校验和有个特点,计算的时候把一个“伪头部”临时加进数据里参与求和,伪头部包含源IP、目的IP、协议号和TCP长度信息。为什么要这么设计?因为TCP和UDP都属于“端到端”传输,它们得确保数据从源头到目的地都没被路由设备改坏,而不只是传输段内没出错。如果IP层判断错了协议或端口,即使数据本身没问题,也会交给错误的进程,伪头部就是为了抵御这种错配。
搞应用层研发的人,平时不直接算CRC或Checksum,但抓包排查时一定要能看懂这些字段的位置和作用。特别是当别人跟你说“数据没问题,是不是你应用层解析错了”时,你如果会看报文,就能快速判断题到底是下层错误、网络设备丢包,还是应用协议解析异常,而不是靠感觉甩锅。
4.3 应用层也有自己的完整性校验:哈希、摘要与 TLS
讲到这里,很多做开发的同学会说:应用层好像不做CRC,那怎么保证数据完整性?其实应用层有自己的方案,而且比链路层的简单校验更严格。最常见的做法是给文件或者报文算一个哈希摘要,比如MD5、SHA-256,比对双方各自算出来的摘要,一致就说明数据完整。很多软件下载站给安装包附上校验值,就是这个思路。
HTTP协议层面也有类似机制:早期有Content-MD5头(存在安全风险,现在已不推荐单独用),现代Web安全传输主要靠TLS。TLS在应用层和传输层插入一层加密与完整性保护,每条记录都带一个MAC标签,接收方如果算出来不一致,会直接拒绝终端处理。所以你现在访问一个HTTPS网站,数据完整性和防篡改是TLS层负责的,不需要你在应用层再去重算摘要。
这里要给一个排查建议:上层应用做数据完整性校验时,不要只依赖协议栈,重要数据可以在应用层加一层自己的校验字段。比如请求体里附一个签名字段,用双方约定的密钥对关键字段做HMAC。核心交易场景尤其如此,因为你不知道中间环节有没有代理、网关在改写数据。应用层校验是最后一道防线,命中的例子少,但一旦漏掉这件事,出事就是大事故。
5. 期末、408与面试:应用层考的不是记忆,是“设计动机”
5.1 教材选择:谢希仁版和“自顶向下”版到底差在哪
关于408和期末复习,很多人纠结“谢希仁教材”和“计算机网络自顶向下”选哪个。简单说:谢希仁版采用自底向上的编排,先讲物理层、链路层再讲到应用层,符合“从细节到整体”的学习直觉,是国内考试复习的主流。而《计算机网络:自顶向下》一开始就讲应用层,先让你知道“网络到底能为用户做什么”,再逐层往下追问“为什么要这么设计”。
我的建议是:如果是准备期末或408考试,以谢希仁的体系为准,先保证知识点覆盖;但学完每一章,再翻一下自顶向下里对应章节的“问题和动机”,补“为什么”。考试题里有很多“为什么这么设计”类的简答题、论述题,这类题往往不是教材原话,而是考察你对设计动机的理解。单纯背概念的人,遇到“请解释TCP为什么需要三次握手”还好,但一遇到“DHCP为什么使用广播”这类新角度,就会卡壳。
5.2 高频考点其实是同几个底层逻辑的变形
应用层的考点看着很多,列出来无非这些:HTTP请求方法和状态码、GET和POST区别、Cookie和Session、DNS解析过程、DHCP四步交互、FTP双连接、邮件协议SMTP/POP3/IMAP、HTTP与HTTPS的区别、HTTP/1.1与HTTP/2的区别。你如果只看表面,会觉得每个都是孤立的记忆点,但往深一层,它们背后都是“协议设计在特定约束下做出的取舍”。
| 考点 | 常见问法 | 真正想考的东西 |
|---|---|---|
| HTTP 状态码 | 503 和 502 的区别 | 是否理解网关、代理、服务不可用的层级关系 |
| GET vs POST | POST 能不能幂等 | 是否理解HTTP设计语义,而不是“长度不同”这类实现差异 |
| Cookie vs Session | 为什么需要 Session | 是否理解无状态协议为什么要引入状态管理 |
| DNS 查询过程 | 输入URL后DNS怎么解析 | 是否理解递归查询、迭代查询、缓存的配合 |
| HTTP vs HTTPS | HTTPS 为什么安全 | 是否理解TLS在应用层与传输层之间的位置 |
比如“GET和POST有什么区别”这种题,网上有无数人回答“GET参数在URL里,POST在Body里”,但这个答案既不全面也不准确。准确的理解是:GET用于请求资源,应当幂等、安全,可以被缓存;POST用于提交资源修改,通常不是幂等的。至于参数位置只是约定,不是协议强制的。答题时只要说出语义差异,面试官就知道你是真的理解HTTP,而不是背了一堆面试题。
5.3 “输入URL到显示页面”为什么每年都考
408也好,大厂面试也好,都喜欢出一道包含万物的综合题:在浏览器里输入一个网址并回车,到页面显示出来,中间发生了什么。很多人把这道题当成“八股”,但其实它考察的是整个网络协议栈在你脑子里的完整链条,最容易暴露“只会分层概念,不知道层与层怎么协作”的问题。
完整的回答大致分成几段:第一段,浏览器先解析URL,提取出协议、域名、端口和资源路径;第二段,域名拿去DNS查询,期间可能命中浏览器缓存、操作系统缓存、hosts文件,也可能走完整的递归查询;第三段,拿到IP后,浏览器与服务器建立TCP连接,其中涉及三次握手,如果访问的是HTTPS,还要经历TLS握手,完成证书校验和密钥协商;第四段,连接建立后浏览器发送HTTP请求,经过代理、负载均衡一路到后端应用,后端处理好返回HTML;第五段,浏览器收到响应开始解析渲染,发现里面还有CSS、JS、图片链接,又重复上面的步骤发起新的HTTP请求。
这道题能一直考下去的原因就在这里:它从不限定“只考应用层”或“只考传输层”,而是逼你把协议栈变成一个动态过程,重新组织一次。答得好不好,直接反映你有没有真正建立“网络全链路”的概念。
5.4 面试里真正加分的一层认知
如果面试官问应用层相关,除了概念,真正加分的点是你能不能举出实际的踩坑案例。比如你说“我调过一个第三方接口,对方一直504,后来发现是DNS解析慢导致连接迟迟建立不了”,这句话比背十遍“HTTP状态码504是网关超时”都管用。面试官想看的不是你能不能背定义,而是你遇到问题时的排查路径:从应用日志到连接耗时、再到DNS解析耗时,一步步拆。
还有一个小技巧:说任何协议问题时,尽量把“设计动机”和“代价”一起说出来。比如讲HTTPS,不能只说“加密了所以安全”,还要说清楚因为它增加了握手开销、TLS记录头开销,所以性能比HTTP更重,这也是HTTP/3要优化握手的原因。能讲出权衡,才说明你真的理解协议,而不是停留在宣传语层面。
6. 应用层开发避坑心法:超时、重试、幂等与连接管理
6.1 超时是一套组合参数,不是单个数值
应用层开发里最容易被低估的就是超时设置。很多人一个接口的HTTP客户端只设了一个“总超时”就完事了,但实际生产环境里,超时必须拆开设:连接超时、读取超时必须分开。连接超时代表“TCP握手多长时间算失败”,一般设几百毫秒到1秒就够;读超时代表“我发了请求之后,多久没收到响应就算失败”,这个要根据业务接口的耗时来定,可能2秒、5秒甚至更长。
把它们混在一起有个很典型的坑:给一个慢接口设了10秒总超时,但网络抖动导致TCP握手连续重试,后面所有请求全堆积在“建连”阶段,等业务真正处理时已经超时了。正确做法是连接超时短一些,快速失败,避免无效等待;读超时长一些,给慢业务留足时间。这个细节,面试里不好直接考,但线上事故排查时一查一个准。
6.2 重试必须带退避和幂等,否则就是二次事故
应用层重试,是我见过最多“好心办坏事”的地方。某个功能掉了一个第三方接口,代码里写了个for循环重试5次,结果第三方服务只是瞬时抖动,流量高峰的时候这5次重试直接把对方打垮。重试的正确姿势是加指数退避:第一次失败后等200ms再试,第二次等400ms,第三次等800ms,最多到几秒钟就停止。还要加随机抖动,防止大量请求在同一时间点一起重试。
比退避更重要的前提是幂等。如果你发起的是一次扣款请求,失败后盲目重试,对方可能已经成功处理了,只是响应超时,最后变成重复扣款。所以应用层做重试前,必须先想清楚这个操作重放一遍会不会有副作用。常见的解法是请求头里带一个业务幂等键,服务端用这个键做去重。这个设计属于典型的应用层职责,传输层和网络层根本不关心你重不重试,它们只会忠实地帮你把同样的请求再送一次。
6.3 长连接、连接池与报文大小的取舍
很多应用层开发者知道要用连接池,但不太清楚为什么。TCP建立连接有三次握手的开销,TLS还要再握一次手,如果每个请求都新建连接,光握手开销就能吃掉大量性能。所以HTTP/1.1引入持久连接,客户端和服务端之间复用同一个TCP连接处理多个请求,连接池就是长期维护一组可用连接的机制。不过要注意,连接池里的连接也不是永葆青春,长时间空闲的服务端可能已经把它断掉了,客户端如果不做保活和失效重连,就会遇到“连接池里拿了个死连接,请求发出去没响应”的玄学故障。
报文大小同样值得关注。HTTP协议对请求行、请求头、Body的大小通常有限制,但很多人的代码根本没处理过“响应体过大”的问题。比如一个接口本来设计返回100条数据,某天数据量膨胀到10万条,响应体一变大,客户端解析超时、内存占用飙升、网关返回413这类状况全来了。在应用层设计接口时,最好明确响应体上限,给列表接口都做分页,别赌数据量永远不变。
6.4 序列化选型与线上监控清单
序列化方式也是应用层的一个经典取舍。JSON的优势是直观、调试方便,随便一个curl命令就能看到结果;Protobuf的优势是体积小、解析速度快,但肉眼不可读,排查问题必须依赖工具。我做过的多数业务系统,对外接口都用JSON,方便外部对接;内部服务之间如果对性能敏感,会换Protobuf。选型没有标准答案,但要清楚代价:JSON的灵活性和可读性是用更多带宽和CPU换来的,Protobuf则正好相反。
最后说一个我自己的习惯:每次新服务上线前,都会确认监控面板上有没有这几项指标——请求量、错误率、P99延迟、上游依赖的响应时间、DNS解析耗时。前三个很好理解,后两个很多团队会漏。上游依赖慢,会让你的P99瞬间恶化;DNS解析耗时异常,则可能意味着递归查询链路出了问题。这些指标看起来是运维的事,但每一项都和你在应用层写的代码直接相关:超时设置不合理,错误率就会异常;重试策略太过激进,上游依赖响应时间就会被拉爆。应用层的所有“参数”,最后都会以线上指标的形式写在你脸上。
我自己的体会是,应用层这个东西,入门靠背协议,进阶靠踩坑。写接口写多了、被超时和重试坑过几轮之后,再看教材,才发现每一行字背后都藏着设计者的权衡。希望这篇东西能帮你在复习或者开发的时候,少走一点弯路。