描述一下打开百度首页后发生的网络过程
图片来自小林 coding
面试官您好,当我们在浏览器地址栏输入www.baidu.com
并按下回车后,直到看到百度首页,这背后其实发生了一场跨越了整个TCP/IP协议栈的、极其精密和复杂的“数据之旅”。
这个旅程,我通常会把它分为以下几个大的阶段:
第一阶段:应用层的准备 —— “翻译地址与准备信件”
URL解析与转义:
- 首先,浏览器会解析我们输入的URL,确定协议是HTTP还是HTTPS,主机名是
www.baidu.com
,以及请求的资源路径。 - 它还会检查URL中是否有非法字符,并进行必要的转义。
- 首先,浏览器会解析我们输入的URL,确定协议是HTTP还是HTTPS,主机名是
DNS解析 (将域名翻译成IP地址):
- 计算机在网络上通信,靠的是IP地址,而不是域名。所以,浏览器必须先找到
www.baidu.com
对应的IP地址。 - 缓存检查(由近及远):
a. 它会先查看浏览器自身的DNS缓存。
b. 如果没有,就查看操作系统的DNS缓存(比如hosts文件)。
c. 如果还没有,就向本地DNS服务器(通常由ISP提供)发起请求。 - 递归与迭代查询:如果本地DNS服务器也没有缓存,它就会启动一个从根DNS服务器 -> .com顶级域DNS服务器 -> baidu.com权威DNS服务器的逐级查询过程,最终获取到百度的IP地址。
- 计算机在网络上通信,靠的是IP地址,而不是域名。所以,浏览器必须先找到
第二阶段:传输层与网络层的准备 —— “建立可靠的通话线路”
现在,我们有了“收件人”的IP地址,需要建立一条可靠的通信管道。
ARP协议获取MAC地址:
- IP地址用于在整个互联网中寻址,而MAC地址则用于在同一个局域网(LAN) 内进行通信。
- 浏览器需要将数据包发给网关(路由器),让它帮忙转发出去。因此,它会通过ARP协议,在局域网内广播:“谁是网关IP xxx.xxx.xxx.xxx?请告诉我你的MAC地址。”
- 网关收到后,会回复自己的MAC地址。
TCP三次握手 (建立连接):
- 浏览器(客户端)会向百度的IP地址和HTTP/HTTPS的默认端口(80/443),发起一个TCP连接请求。
- 通过经典的 “SYN -> SYN+ACK -> ACK” 的三次握手过程,客户端和服务器之间就建立起了一条可靠的、全双工的TCP连接通道。
(如果是HTTPS) TLS握手 (加密通道):
- 如果访问的是HTTPS,那么在TCP连接建立之后,还需要进行一次SSL/TLS的握手。
- 这个过程,双方会验证服务器证书的真伪,并协商出一个用于后续通信的对称加密密钥,确保所有传输的数据都是加密的、安全的。
第三阶段:HTTP通信 —— “正式收发信件”
发送HTTP请求:
- 现在,万事俱备。浏览器会构造一个HTTP请求报文(比如
GET / HTTP/1.1 ...
),并通过刚刚建立好的、安全的TCP连接,发送给百度服务器。
- 现在,万事俱备。浏览器会构造一个HTTP请求报文(比如
服务器处理与响应:
- 百度的服务器(通常是Nginx等Web服务器)接收到请求后,可能会进行负载均衡,将请求转发给后端的业务服务器。
- 业务服务器处理请求,生成要返回的HTML页面内容。
- 然后,服务器会构造一个HTTP响应报文(比如
HTTP/1.1 200 OK ...
),将HTML内容放在响应体中,再通过TCP连接返回给浏览器。
第四阶段:浏览器渲染页面
- 解析HTML:浏览器在收到HTML响应后,开始从上到下解析,构建DOM树。
- 请求额外资源:在解析过程中,如果遇到
<img>
,<link rel="stylesheet">
,<script>
等标签,浏览器会发现还需要加载图片、CSS、JavaScript等额外资源。 - 并发请求:浏览器会再次、并发地发起多个HTTP请求,去获取这些资源。在HTTP/1.1的长连接下,它会复用之前建立的TCP连接;在HTTP/2下,它会在一个连接上多路复用这些请求。
- 构建渲染树与绘制:当CSS加载并解析完成后,会与DOM树合并,构建成渲染树(Render Tree)。然后,浏览器进行布局(Layout)和绘制(Paint),最终,我们将会在屏幕上看到五彩斑斓的百度首页。
- TCP四次挥手:当所有资源都加载完毕,并且连接空闲了一段时间后,浏览器或服务器会发起TCP四次挥手,优雅地关闭连接。
这一整套流程,就是我们按下回车后,在背后发生的、横跨了整个网络协议栈的壮丽史诗。
网页非常慢转圈圈的时候,要定位问题需要从哪些角度?
面试官您好,当遇到“网页打开很慢,一直在转圈”这种问题时,我会遵循一个自底向上、由近及远的系统性排查思路,来逐步缩小问题的范围,最终定位到根源。
这个排查过程,可以分为以下几个大的步骤:
第一步:界定问题范围 —— 是“我的问题”还是“别人的问题”?
正如您所说,第一步是确定故障的大致范围。
- 检查自身网络连通性:
- 我会先尝试访问几个其他的、公认稳定的大网站(如
baidu.com
,google.com
)。 - 如果都打不开:那大概率是我本地的网络环境出了问题。我会检查:
- 物理连接:Wi-Fi是否连接正常?网线是否插好?
- 本地网络配置:IP地址、DNS服务器配置是否正确?
- 路由器/光猫是否工作正常?
- 如果其他网站都能正常、快速地打开:那就排除了我本地网络的问题,说明问题出在从我这里,到目标网站的这条链路上,或者是目标网站本身。
- 我会先尝试访问几个其他的、公认稳定的大网站(如
第二步:网络层面的排查 —— “路通不通?”
现在,我会开始使用网络工具,按照网络协议栈的层次,从下往上进行排查。抓包(使用Wireshark
或tcpdump
) 是这个阶段最核心、最有效的手段。
DNS解析是否成功?
- 排查点:首先,要看我们的计算机是否能把网站的域名,成功解析成IP地址。
- 如何排查:
- 在抓包结果中,筛选
dns
协议,看是否有正常的DNS查询和响应。 - 或者直接使用
nslookup www.target.com
或dig www.target.com
命令,看能否返回IP地址。
- 在抓包结果中,筛选
- 可能的问题:
- 如果解析失败,可能是域名输错了,或者本地DNS服务器出了问题。
- 如果解析很慢,可能是本地DNS缓存污染,或者DNS服务器响应慢。
TCP连接是否能建立?(三次握手)
- 排查点:在拿到IP地址后,我们的浏览器需要与服务器建立一个TCP连接。
- 如何排查:在抓包结果中,筛选
tcp
协议,并重点观察三次握手的过程。 - 可能的问题:
- 只看到
SYN
包发出,但收不到SYN+ACK
:这通常意味着我们的请求包,在去往服务器的路上被防火墙拦截了,或者服务器因为负载过高、遭受SYN Flood攻击等原因,无法响应。 - 能看到
SYN+ACK
,但连接最终失败:可能是网络不稳定,或者中间网络设备有问题。
- 只看到
第三步:应用层面的排查 —— “服务器说了什么?”
如果TCP连接能成功建立,说明网络通路是基本正常的。问题就出在HTTP/HTTPS这个层面了。
- HTTP请求是否已发送?响应是什么?
- 排查点:在抓包结果或浏览器开发者工具(F12)的“网络(Network)”面板中,查看HTTP的交互。
- 可能的问题:
- 请求长时间处于“Pending”状态:这可能意味着浏览器因为同域名下的连接数限制,正在等待一个可用的TCP连接。
- 服务器长时间不返回响应(TTFB过长):这明确地指向了服务器端性能瓶颈。可能是数据库慢查询、后端代码逻辑复杂、或者服务器负载过高导致的。此时,就需要去排查服务器端的日志和性能监控了。
- 服务器返回了错误的状态码:
4xx
客户端错误:如404 Not Found
(URL路径错了)、403 Forbidden
(没权限)、401 Unauthorized
(没登录)。5xx
服务器端错误:如500 Internal Server Error
(服务器代码Bug)、502 Bad Gateway
(网关或代理问题)、504 Gateway Timeout
(上游服务超时)。
第四步:前端与网络质量排查 —— “数据回来了,为什么还慢?”
如果服务器很快地返回了200 OK
和HTML内容,但页面依然在转圈,那么问题可能出在以下几个方面:
前端渲染问题:
- 正如您提到的,可能是JavaScript代码执行出错,阻塞了页面的渲染。此时需要查看浏览器控制台的报错信息。
- 也可能是页面需要加载的资源(JS, CSS, 图片)过多、过大,或者这些资源所在的CDN节点很慢。
网络质量问题(丢包与重传):
- 排查点:网络链路虽然通,但质量很差。
- 如何排查:在抓包结果中,观察是否有大量的TCP重传(Retransmissions)、重复的ACK等现象。
- 原因:大量的丢包和重传,会导致数据传输的实际速度非常慢,即使服务器响应很快,数据也迟迟无法完整地到达浏览器。
总结一下,我的排查思路就是这样一个自外而内、逐层深入的过程:先看自己 -> 再看路(DNS/TCP)-> 再看对方(HTTP)-> 最后看数据传输质量和前端渲染。通过这样系统性的分析,绝大多数的“转圈圈”问题,都能被精准地定位到。
Server A和Server B,如何判断两个服务器正常连接?出错怎么办?
面试官您好,您提出的这个问题非常好,它涉及到在分布式系统中,如何判断两个服务之间的连接是否健康、有效,以及在出现问题时如何处理。
要判断两个服务器(我们称之为A和B)之间的TCP连接是否正常,并处理异常,我们通常有两种层面的解决方案:应用层心跳和TCP层Keepalive。
方案一:应用层心跳 (Application-Level Heartbeat) —— 最常用、最灵活的方案
这是我们在实践中最常用、也最推荐的方式。
如何实现?
- 在A和B之间建立连接后,由一方(比如客户端A)定期地(比如每隔5秒)向另一方(服务器B)发送一个自定义的、非常小的“心跳”数据包。这个包的内容可以很简单,比如就是一个字符串
"ping"
。 - 服务器B在收到心跳包后,立即回复一个对应的“心跳响应”(比如
"pong"
)。 - 判断逻辑:
- 发送方(A):如果在发送了N次心跳包后(比如连续3次),都没有收到对方的
pong
响应,那么A就可以单方面地认为这个连接已经失效,或者B服务已经无响应了。此时,A应该主动关闭这个连接,并尝试进行重连或将B节点标记为不可用。 - 接收方(B):同样,如果B在超过一个约定的时间(比如超过3个心跳周期,即15秒)都没有收到来自A的任何心跳包,那么B也可以认为A已经“失联”,然后主动关闭这个连接以释放资源。
- 发送方(A):如果在发送了N次心跳包后(比如连续3次),都没有收到对方的
- 在A和B之间建立连接后,由一方(比如客户端A)定期地(比如每隔5秒)向另一方(服务器B)发送一个自定义的、非常小的“心跳”数据包。这个包的内容可以很简单,比如就是一个字符串
优点:
- 灵活性极高:心跳的频率、超时时间、重试次数等,都可以在我们的应用程序中完全自定义,可以根据业务对实时性的要求,设置得非常灵敏(比如1秒一次)。
- 能检测应用层面的“假死”:即使底层的TCP连接是通的,但如果服务器B的业务逻辑卡死了,无法响应心跳,客户端A也能及时地发现。
应用:几乎所有的RPC框架(如Dubbo)、消息队列(如RocketMQ)、数据库连接池,其内部都实现了应用层的心跳保活机制。
方案二:TCP Keepalive机制 —— 内核级的“兜底”方案
这是TCP协议自身提供的一种连接保活机制,由操作系统内核来管理。
它解决了什么问题?
- 它主要是为了解决 “半打开连接” 的问题。比如,客户端A突然断电宕机,它无法向服务器B发送任何关闭连接的
FIN
包。此时,在服务器B看来,这个TCP连接依然处于ESTABLISHED
状态,它会永远地等待下去,造成资源泄漏。
- 它主要是为了解决 “半打开连接” 的问题。比如,客户端A突然断电宕机,它无法向服务器B发送任何关闭连接的
如何工作?
- 我们可以通过Socket选项来开启TCP Keepalive。
- 当一个连接上长时间没有任何数据交互时(这个“长时间”在Linux上默认是2小时,
tcp_keepalive_time
),内核会自动介入。 - 内核会发送一个 “保活探测包”(Keepalive Probe) 给对端。这个包不包含任何应用数据。
- 判断逻辑:
- 如果收到对方的
ACK
响应,说明连接还活着,计时器重置。 - 如果没有收到响应,内核会每隔一段时间(默认75秒,
tcp_keepalive_intvl
)重试一次。 - 在连续重试多次(默认9次,
tcp_keepalive_probes
)后,如果依然没有任何响应,内核就会最终认定这个连接已经死亡。
- 如果收到对方的
- 处理:内核会强制关闭这个连接(类似发送RST),并通知上层应用程序连接已断开。
缺点:
- 默认超时时间太长:默认2小时的空闲时间才开始探测,对于绝大多数需要快速感知故障的互联网应用来说,太慢了,不实用。
- 不感知应用层状态:它只能检测到TCP层面的连接存活,无法感知到应用进程是否已经卡死。
总结与选型
特性 | 应用层心跳 | TCP Keepalive |
---|---|---|
实现层面 | 应用程序 | 操作系统内核 |
灵活性 | 高 (频率、超时可自定义) | 低 (全局内核参数) |
探测实时性 | 高 (可达秒级) | 低 (默认小时级) |
感知能力 | 可感知应用“假死” | 只能感知TCP连接存活 |
主要用途 | 主动、快速的连接健康检查 | 被动的、兜底的僵尸连接清理 |
在实践中,我们通常会优先使用应用层心跳,因为它更可控、更灵敏,能更好地满足业务对高可用和快速故障发现的需求。而TCP Keepalive,则更多地是作为一个最后的、底层的“保险丝”,来清理那些因各种意外而产生的、长时间无人问津的“僵尸连接”。
服务端正常启动了,但是客户端请求不到有哪些原因?如何排查?
面试官您好,当遇到“客户端请求不到已启动的服务端”这类问题时,我会遵循一个从网络连通性到应用层逻辑的、逐层排查的思路,来系统地定位问题根源。
这个问题可以分为两大类情况。
第一类:请求完全失败,没有任何响应(如连接超时、无法访问)
这种情况,通常意味着问题出在网络层或传输层,即客户端和服务器之间 “路不通”。我会按照以下顺序进行排查:
第一步:检查网络基础配置(客户端侧)
- IP地址和端口号是否正确? 这是最基本、但也最容易出错的地方。我会仔细核对请求的IP和Port是否与服务端监听的完全一致。
- 本地网络是否正常? 我会先
ping
一个公认的地址(如ping baidu.com
)来确认我自己的机器网络是通的。 - 是否设置了网络代理? 检查我的浏览器或测试工具,是否配置了HTTP代理,而这个代理本身可能无法访问目标服务器。
第二步:检查网络连通性(端到端)
ping <服务器IP>
:首先,用ping
命令检查我本地到服务器之间的网络路由是否可达。如果ping
不通,说明中间可能存在网络设备故障或路由配置问题。telnet <服务器IP> <端口号>
:这是最关键的一步。ping
通只能说明网络可达,但不能保证端口是开放的。telnet
可以精确地测试服务器的特定端口是否在监听。- 如果
telnet
连接成功(黑屏),说明网络链路和服务器端口都是通的,问题很可能在应用层。 - 如果
telnet
连接超时或被拒绝,那么问题几乎可以锁定在防火墙或服务器应用本身。
- 如果
第三步:检查防火墙与安全组(服务端侧)
- 如果
telnet
不通,我会首先怀疑防火墙。 - 服务器防火墙:登录服务器,检查其自身的防火墙(如Linux的
iptables
或firewalld
)是否开放了对应的端口。 - 云服务商安全组:如果服务器部署在云上(如阿里云、AWS),还需要检查其安全组规则,是否允许我的客户端IP地址访问目标端口。这是非常常见的被忽略的点。
- 如果
第二类:请求有响应,但返回的是错误状态码
这种情况,说明网络链路是通的,问题出在应用层。我会根据返回的具体HTTP状态码,进行针对性的排查。
400 Bad Request
:我会检查我发送的请求参数,比如JSON格式是否正确、请求头是否缺失、参数类型是否匹配等。401 Unauthorized
:我会检查我的请求是否携带了正确的认证信息,比如请求头中的Authorization
Token是否有效、是否过期。403 Forbidden
:如果认证信息是正确的,但依然返回403,我会去检查:- 当前用户的角色和权限,是否足以访问这个接口。
- 服务器或中间件(如Nginx、Tomcat)是否有IP白名单或访问控制的配置,拒绝了我的请求。
404 Not Found
:这是最常见的错误之一。我会检查:- 请求的URL路径是否拼写正确。
- 服务器的应用**上下文路径(Context Path)**是否配置正确。
- 后端的Controller中,对应的**
@RequestMapping
路径**是否匹配。
500 Internal Server Error
:- 看到500,就意味着问题100%出在服务端。
- 我会立即登录服务器,查看应用程序的错误日志(Error Log)。日志中通常会明确地打印出异常堆栈,比如空指针、数据库异常等,根据日志就能快速定位到代码中的Bug。
502/503/504
(网关相关错误):- 如果架构中使用了Nginx等反向代理,出现这些错误,通常意味着Nginx与后端的Tomcat等应用服务器之间的通信出了问题。
- 我会去检查:
- 后端应用服务是否已经宕机或正在重启。
- 后端服务是否因为负载过高、GC停顿等原因,导致处理请求过慢,从而引发了Nginx的网关超时。
通过这样一套从网络到应用、从客户端到服务端的、结构化的排查流程,绝大多数的“请求不通”问题,都能被高效地定位和解决。
服务器ping不通但是HTTP能请求成功,会出现这种情况吗?什么原因造成的?
面试官您好,您提出的这个问题非常好,它揭示了网络连通性测试的一个常见“陷阱”。
答案是:会的,这种情况完全可能出现,而且在生产环境中相当常见。
1. 根本原因:ping
和HTTP使用了不同的网络协议
这个现象的根本原因在于,ping
命令和HTTP请求,在网络协议栈的传输层和网络层,走的是完全不同的路径。
ping
命令:它使用的是ICMP协议(Internet Control Message Protocol,互联网控制报文协议)。ICMP的主要作用,是在IP主机、路由器之间传递控制消息,比如网络是否可达、主机是否可访问等。它更像是一个网络的“侦察兵”或“诊断工具”。HTTP请求:它工作在应用层,而其底层的传输,依赖的是TCP协议。我们访问一个网页,是建立在一次完整的TCP连接之上的。
所以,“ping不通”只代表了服务器的ICMP端口对我们不可达,但这完全不代表它的TCP端口也不可达。
2. 最常见的原因:服务器防火墙策略
防火墙策略,是导致这种情况最核心、最常见的原因。
为什么会这么配置?
- 出于安全考虑,很多服务器的系统管理员或云服务商,会配置非常严格的防火墙或安全组规则。
- ICMP协议,虽然对网络诊断很有用,但它也可能被用于一些网络攻击,比如ICMP扫描(用于探测网络拓扑和存活主机)和ICMP洪水攻击(Smurf Attack)。
- 因此,一个常见的安全实践就是,在服务器的防火墙上,“一刀切”地禁用所有传入的ICMP请求,即“禁止ping”。
- 但是,为了让Web服务能够正常工作,它又必须明确地开放TCP的80端口(HTTP)和443端口(HTTPS)。
最终结果:
- 当我们
ping
这台服务器时,我们的ICMP请求包,在到达服务器后,被其防火墙直接丢弃了,所以我们永远收不到回应,表现为“ping不通”。 - 但当我们用浏览器访问它的网页时,我们的TCP连接请求,因为其目标端口是80或443,被防火墙允许通过,所以能够正常建立连接,HTTP请求也能成功。
- 当我们
3. 其他可能的原因
虽然防火墙是主要原因,但也存在其他一些可能性:
- 负载均衡器的配置:一些负载均衡器(如LVS)可能被配置为只转发特定TCP/UDP端口的流量,而不会响应或转发ICMP请求。
- 操作系统内核参数:在Linux中,也可以通过内核参数(如
net.ipv4.icmp_echo_ignore_all
)来全局禁用对ping请求的响应。
反向思考:Ping得通,但HTTP不通
反过来,“ping得通,但HTTP不通” 也是非常常见的故障场景。这通常意味着:
- 网络链路是通的,ICMP协议也是允许的。
- 但服务器的80/443端口没有被监听(比如Web服务没启动、或者监听在了错误的端口上),或者防火墙阻止了对这个特定端口的访问。
总结一下,ping
只是一个基础的网络连通性探测工具,它的成功与否,并不能完全等同于特定应用服务的可用性。在排查网络问题时,我们应该使用更具针对性的工具,比如用telnet <ip> <port>
来测试特定端口的TCP连通性,这比ping
要可靠得多。
参考小林 coding