网络与操作系统基础
网络和操作系统是架构师排查线上问题的底层功力。本模块覆盖TCP/IP、HTTP/HTTPS、IO模型、Linux内核调优等核心知识点,共60题。
难度标记
- 🔵 高级(Senior):8-10年经验应该能答好
- 🔴 专家(Expert):需要深入的实战经验
- ⚫ 大师(Master):开放性问题
一、TCP/IP协议(1-20题)
1. 🔵 TCP三次握手的过程和每一步的作用?
答:三次握手建立可靠连接。
1 | 客户端 服务端 |
为什么是三次而不是两次:
- 两次握手无法防止历史连接的建立
- 场景:客户端发送的旧SYN延迟到达,服务端误以为是新连接
- 三次握手时客户端会发现序列号不对,发送RST终止
为什么不是四次:
- 三次已经足够双方确认各自的发送和接收能力
- 第二步合并了SYN和ACK,减少一次交互
2. 🔵 TCP四次挥手的过程?为什么需要TIME_WAIT?
答:四次挥手关闭连接。
1 | 客户端 服务端 |
TIME_WAIT的作用:
- 确保最后一个ACK能到达服务端(如果丢失,服务端会重发FIN)
- 让本连接的所有报文在网络中消失,防止影响新连接
TIME_WAIT过多的问题:
- 占用端口和内存
- 解决方案:
net.ipv4.tcp_tw_reuse=1(允许复用TIME_WAIT连接) net.ipv4.tcp_tw_recycle=1(快速回收,NAT环境下有问题,不推荐)
3. 🔴 TCP的拥塞控制算法有哪些?实际生产中如何选择?
答:拥塞控制是TCP性能的核心。
经典算法:
- 慢启动:cwnd从1开始指数增长,到达ssthresh后进入拥塞避免
- 拥塞避免:cwnd线性增长
- 快重传:收到3个重复ACK立即重传
- 快恢复:快重传后cwnd减半,进入拥塞避免(不回到慢启动)
现代算法:
- CUBIC:Linux默认,基于三次函数调整cwnd,对高延迟网络友好
- BBR:Google开发,基于带宽和RTT估算,不依赖丢包信号
- 优势:在有一定丢包率的网络中表现远好于CUBIC
- 适合:长肥管道(高带宽高延迟)、有一定丢包的网络
- 开启:
sysctl net.ipv4.tcp_congestion_control=bbr
生产建议:
- 内网通信:CUBIC(默认即可)
- 跨机房/跨国:BBR(显著提升吞吐量)
- CDN/直播:BBR
4. 🔴 TCP粘包和拆包是什么?如何解决?
答:TCP是字节流协议,没有消息边界的概念。
粘包:多个消息被合并成一个TCP段发送
拆包:一个消息被拆分成多个TCP段发送
解决方案:
- 固定长度:每个消息固定N字节(简单但浪费)
- 分隔符:用特殊字符分隔消息(如HTTP的\r\n)
- 长度前缀:消息头包含消息体长度(最常用)
1
[4字节长度][消息体]
- 自定义协议:如Dubbo协议、Redis协议
Netty中的解决方案:
FixedLengthFrameDecoder:固定长度DelimiterBasedFrameDecoder:分隔符LengthFieldBasedFrameDecoder:长度前缀(推荐)
5. 🔴 TCP的Keep-Alive和应用层心跳有什么区别?
答:两者都是检测连接存活,但层次和目的不同。
TCP Keep-Alive:
- 操作系统层面,默认2小时发一次探测包
- 参数:
tcp_keepalive_time=7200、tcp_keepalive_intvl=75、tcp_keepalive_probes=9 - 只能检测连接是否存活,不能检测应用是否正常
应用层心跳:
- 应用自己实现,通常30秒-60秒
- 可以携带业务信息(如负载、状态)
- 可以检测应用层的健康状态
- 更灵活,可以自定义超时和重试策略
生产建议:
- 两者都要用
- TCP Keep-Alive作为兜底(调短到几分钟)
- 应用层心跳作为主要检测手段
6. 🔵 TCP和UDP的区别?UDP的应用场景?
答:
| 维度 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠性 | 可靠传输 | 不可靠 |
| 有序性 | 保证有序 | 不保证 |
| 流量控制 | 有 | 无 |
| 拥塞控制 | 有 | 无 |
| 头部开销 | 20字节 | 8字节 |
| 传输效率 | 较低 | 高 |
UDP应用场景:
- DNS查询(短小、快速)
- 视频/音频流(允许丢包,低延迟优先)
- 游戏(实时性要求高)
- QUIC协议(基于UDP实现可靠传输)
7. 🔴 什么是QUIC协议?为什么HTTP/3选择了QUIC?
答:QUIC是Google开发的基于UDP的传输协议。
QUIC的优势:
- 0-RTT建连:首次1-RTT,后续0-RTT(TCP+TLS需要3-RTT)
- 多路复用无队头阻塞:HTTP/2的多路复用在TCP层仍有队头阻塞,QUIC在UDP上实现独立的流
- 连接迁移:基于Connection ID而非四元组,WiFi切4G不断连
- 内置加密:TLS 1.3集成在协议中
- 改进的拥塞控制:可以在用户空间快速迭代
8. 🔴 什么是TCP的队头阻塞(Head-of-Line Blocking)?
答:队头阻塞是TCP的固有问题。
TCP层面:
- TCP保证有序交付,如果一个包丢失,后续所有包都要等待重传
- 即使后续包已经到达,也不能交给应用层
HTTP/2层面:
- HTTP/2在一个TCP连接上多路复用多个请求
- 一个请求的包丢失会阻塞同一连接上的所有请求
- 这就是HTTP/3选择QUIC(基于UDP)的原因
9. 🔵 TCP的滑动窗口机制是什么?
答:滑动窗口实现流量控制和可靠传输。
发送窗口:
- 已发送已确认 | 已发送未确认 | 可以发送 | 不能发送
- 窗口大小 = 接收方通告的窗口大小(rwnd)和拥塞窗口(cwnd)的较小值
接收窗口:
- 接收方通过ACK中的Window字段告知发送方自己的接收能力
- 窗口为0时发送方停止发送(零窗口探测机制防止死锁)
10. 🔴 线上遇到大量CLOSE_WAIT怎么排查?
答:CLOSE_WAIT表示对端已经关闭连接,但本端没有调用close()。
常见原因:
- 应用代码没有正确关闭连接(最常见)
- 连接池配置不当(连接泄漏)
- 应用处理慢,来不及关闭
排查步骤:
1 | # 查看CLOSE_WAIT连接数 |
解决方案:
- 检查代码中HTTP连接、数据库连接是否正确关闭(try-with-resources)
- 检查连接池配置(最大连接数、空闲超时)
- 设置SO_LINGER选项强制关闭
11. 🔴 线上遇到大量TIME_WAIT怎么处理?
答:TIME_WAIT过多通常出现在短连接高并发场景。
原因:主动关闭连接的一方会进入TIME_WAIT,持续2MSL(通常60秒)
解决方案:
- 连接复用:使用长连接/连接池,减少连接创建和关闭
- 内核参数调优:
1
2
3net.ipv4.tcp_tw_reuse = 1 # 允许复用TIME_WAIT连接
net.ipv4.tcp_max_tw_buckets = 5000 # 限制TIME_WAIT数量
net.ipv4.tcp_fin_timeout = 30 # 缩短FIN_WAIT_2超时 - 让服务端主动关闭:调整为客户端主动关闭(TIME_WAIT在客户端)
- 使用SO_REUSEADDR:允许绑定TIME_WAIT状态的端口
二、HTTP/HTTPS(12-25题)
12. 🔵 HTTP/1.1、HTTP/2、HTTP/3的主要区别?
答:
| 特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP | TCP | QUIC(UDP) |
| 多路复用 | 不支持 | 支持 | 支持 |
| 头部压缩 | 无 | HPACK | QPACK |
| 服务端推送 | 无 | 支持 | 支持 |
| 队头阻塞 | 有 | TCP层有 | 无 |
| 建连延迟 | 1-3 RTT | 1-3 RTT | 0-1 RTT |
HTTP/2核心改进:
- 二进制分帧:将HTTP消息分解为帧,交错发送
- 多路复用:一个连接上并行多个请求
- 头部压缩:HPACK算法,减少重复头部传输
- 服务端推送:服务端主动推送资源
13. 🔵 HTTPS的握手过程?
答:HTTPS = HTTP + TLS。
TLS 1.2握手(2-RTT):
1 | 客户端 服务端 |
TLS 1.3握手(1-RTT):
- 合并了多个步骤,只需1个RTT
- 移除了不安全的加密算法
- 支持0-RTT恢复(用之前的密钥直接发送数据)
14. 🔴 HTTPS的证书链验证过程?
答:证书链是信任传递的机制。
1 | 根CA证书(预装在操作系统/浏览器中) |
验证过程:
- 服务端发送自己的证书和中间CA证书
- 客户端用中间CA的公钥验证服务端证书的签名
- 客户端用根CA的公钥验证中间CA证书的签名
- 根CA证书在本地信任库中,验证通过
证书包含的信息:
- 域名(Subject/SAN)
- 公钥
- 有效期
- 签发者
- 数字签名
15. 🔴 什么是SNI?为什么需要SNI?
答:SNI(Server Name Indication)解决一个IP托管多个HTTPS站点的问题。
问题:TLS握手时还没有HTTP请求,服务端不知道客户端要访问哪个域名,无法返回正确的证书。
解决:客户端在ClientHello中携带要访问的域名(SNI扩展),服务端根据域名返回对应证书。
注意:SNI是明文传输的,会暴露访问的域名。ECH(Encrypted Client Hello)是解决方案,将SNI加密。
16. 🔵 HTTP的常见状态码及含义?
答:
| 状态码 | 含义 | 常见场景 |
|---|---|---|
| 200 | 成功 | 正常响应 |
| 201 | 已创建 | POST创建资源成功 |
| 204 | 无内容 | DELETE成功 |
| 301 | 永久重定向 | 域名迁移 |
| 302 | 临时重定向 | 短链跳转 |
| 304 | 未修改 | 缓存命中 |
| 400 | 请求错误 | 参数校验失败 |
| 401 | 未认证 | 未登录 |
| 403 | 禁止访问 | 无权限 |
| 404 | 未找到 | 资源不存在 |
| 429 | 请求过多 | 限流 |
| 500 | 服务器错误 | 代码异常 |
| 502 | 网关错误 | 后端服务不可用 |
| 503 | 服务不可用 | 服务过载 |
| 504 | 网关超时 | 后端服务超时 |
17. 🔴 502和504的区别?线上遇到怎么排查?
答:
- 502 Bad Gateway:网关(Nginx)连接到后端服务,但后端返回了无效响应(通常是后端挂了)
- 504 Gateway Timeout:网关连接后端服务超时(后端处理太慢)
502排查:
- 检查后端服务是否存活
- 检查后端服务的错误日志
- 检查Nginx的upstream配置
504排查:
- 检查后端服务的处理耗时
- 检查是否有慢SQL或外部调用超时
- 调整Nginx的proxy_read_timeout
18. 🔴 HTTP缓存机制有哪些?强缓存和协商缓存的区别?
答:
强缓存(不发请求):
Cache-Control: max-age=3600(优先级高)Expires: Thu, 01 Dec 2025 16:00:00 GMT
协商缓存(发请求验证):
Last-Modified / If-Modified-Since:基于修改时间ETag / If-None-Match:基于内容哈希(优先级高)
流程:
1 | 请求 → 检查强缓存 → 未过期:直接使用(200 from cache) |
19. 🔵 Cookie、Session、Token的区别?
答:
| 维度 | Cookie | Session | Token(JWT) |
|---|---|---|---|
| 存储位置 | 客户端 | 服务端 | 客户端 |
| 安全性 | 较低 | 较高 | 中等 |
| 跨域 | 受限 | 受限 | 灵活 |
| 服务端状态 | 无状态 | 有状态 | 无状态 |
| 扩展性 | 好 | 差(需要共享Session) | 好 |
现代架构推荐:JWT Token
- 无状态,天然支持水平扩展
- 跨域友好
- 缺点:无法主动失效(需要黑名单机制)
20. 🔴 什么是跨域?CORS的工作原理?
答:浏览器的同源策略限制不同源的请求。
同源:协议+域名+端口完全相同。
CORS(Cross-Origin Resource Sharing):
- 简单请求:直接发送,服务端返回
Access-Control-Allow-Origin - 预检请求(OPTIONS):非简单请求先发OPTIONS确认服务端是否允许
服务端配置:
1 | Access-Control-Allow-Origin: https://example.com |
三、IO模型与网络编程(21-35题)
21. 🔵 Linux的五种IO模型是什么?
答:
- 阻塞IO(Blocking IO):read()阻塞直到数据就绪
- 非阻塞IO(Non-blocking IO):read()立即返回,需要轮询
- IO多路复用(IO Multiplexing):select/poll/epoll监听多个fd
- 信号驱动IO(Signal-driven IO):注册信号处理函数,数据就绪时通知
- 异步IO(Asynchronous IO):aio_read()立即返回,内核完成后通知
实际使用最多的是IO多路复用(epoll)。
22. 🔴 select、poll、epoll的区别?为什么epoll性能最好?
答:
| 维度 | select | poll | epoll |
|---|---|---|---|
| fd数量限制 | 1024 | 无限制 | 无限制 |
| fd传递 | 每次全量拷贝 | 每次全量拷贝 | 内核维护,无需拷贝 |
| 事件检测 | 遍历所有fd | 遍历所有fd | 回调通知,O(1) |
| 时间复杂度 | O(n) | O(n) | O(1) |
epoll高性能的原因:
- 红黑树管理fd:增删改查O(log n)
- 就绪链表:只返回就绪的fd,不需要遍历所有
- mmap:内核和用户空间共享内存,减少拷贝
- 边缘触发(ET):只在状态变化时通知,减少系统调用
epoll的两种模式:
- LT(水平触发):只要fd就绪就一直通知(默认,编程简单)
- ET(边缘触发):只在状态变化时通知一次(性能更好,编程复杂)
23. 🔴 Reactor模式是什么?Netty的线程模型?
答:Reactor是高性能网络编程的核心模式。
单Reactor单线程:
- 一个线程处理所有事件(接受连接、读写、业务处理)
- 适合:业务处理快的场景(如Redis)
单Reactor多线程:
- 一个线程处理连接和IO事件
- 业务处理交给线程池
- 问题:单Reactor成为瓶颈
主从Reactor多线程(Netty默认):
1 | MainReactor(BossGroup):接受连接,将新连接注册到SubReactor |
Netty线程模型:
1 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 1个线程接受连接 |
24. 🔴 什么是零拷贝(Zero Copy)?有哪些实现方式?
答:零拷贝减少数据在内核空间和用户空间之间的拷贝次数。
传统IO(4次拷贝):
1 | 磁盘 → 内核缓冲区 → 用户缓冲区 → Socket缓冲区 → 网卡 |
零拷贝方案:
mmap + write(3次拷贝):
- mmap将内核缓冲区映射到用户空间
- 减少一次内核→用户的拷贝
sendfile(2次拷贝,Linux 2.4+):
1
磁盘 → 内核缓冲区 → 网卡(DMA直接传输)
- 数据不经过用户空间
- Java中:
FileChannel.transferTo()
splice(Linux 2.6.17+):
- 在两个fd之间直接传输数据
- 不需要内核缓冲区到Socket缓冲区的拷贝
应用场景:
- Kafka:使用sendfile发送消息给消费者
- Nginx:使用sendfile发送静态文件
- Netty:使用FileRegion封装零拷贝
25. 🔴 Kafka为什么性能这么高?从网络和IO角度分析。
答:Kafka的高性能是多种技术的组合。
- 顺序写磁盘:追加写入,利用磁盘顺序IO(比随机IO快1000倍)
- Page Cache:利用操作系统的页缓存,减少磁盘IO
- 零拷贝:sendfile直接从Page Cache发送到网卡
- 批量处理:消息批量发送和消费,减少网络往返
- 压缩:消息批量压缩,减少网络传输量
- 分区并行:多分区并行读写
四、Linux系统与内核调优(36-50题)
26. 🔴 Linux进程和线程的区别?
答:在Linux中,线程是轻量级进程(LWP)。
| 维度 | 进程 | 线程 |
|---|---|---|
| 地址空间 | 独立 | 共享 |
| 创建开销 | 大(fork) | 小(clone) |
| 切换开销 | 大(TLB刷新) | 小 |
| 通信 | IPC(管道、共享内存) | 直接共享内存 |
| 崩溃影响 | 不影响其他进程 | 影响同进程的所有线程 |
Linux实现:
- 进程和线程都用
task_struct表示 - 线程通过
clone()创建,共享地址空间 - Java线程 = Linux线程(1:1映射)
- Go协程 = 用户态线程(M:N映射)
27. 🔴 CPU飙高怎么排查?
答:这是线上最常见的问题之一。
排查步骤:
1 | # 1. 找到CPU最高的进程 |
常见原因:
- 死循环:代码bug导致无限循环
- 频繁GC:Full GC导致CPU飙高(
jstat -gcutil <pid> 1000) - 正则回溯:复杂正则表达式导致灾难性回溯
- 加密/压缩:大量加密或压缩操作
- 线程竞争:大量线程竞争锁,自旋消耗CPU
28. 🔴 内存泄漏怎么排查?
答:Java内存泄漏排查流程。
1 | # 1. 查看内存使用趋势 |
常见泄漏原因:
- 集合类未清理:静态Map/List持续添加不删除
- 连接未关闭:数据库连接、HTTP连接泄漏
- ThreadLocal未清理:线程池中ThreadLocal不remove
- 监听器未注销:注册了事件监听器但未注销
- 缓存无上限:本地缓存没有设置最大容量和过期时间
29. 🔴 Linux的文件描述符(fd)是什么?fd耗尽怎么处理?
答:fd是Linux中对打开文件、Socket、管道等的抽象引用。
1 | # 查看系统级fd限制 |
fd耗尽的原因:
- 连接泄漏(Socket未关闭)
- 文件未关闭
- ulimit设置太小
解决方案:
1 | # 临时调整 |
30. 🔴 Linux内核参数调优有哪些关键参数?
答:高并发服务器必须调优的参数。
网络相关:
1 | # 最大连接数 |
内存相关:
1 | # 减少swap使用 |
文件系统:
1 | # 文件描述符 |
31. 🔴 什么是Page Cache?对应用性能有什么影响?
答:Page Cache是Linux内核的文件缓存机制。
工作原理:
- 读文件时,内核先将数据读入Page Cache,再拷贝到用户空间
- 下次读同一文件,直接从Page Cache返回(不需要磁盘IO)
- 写文件时,先写入Page Cache(脏页),后台线程异步刷盘
对应用的影响:
- Kafka利用Page Cache实现高性能读写
- MySQL的Buffer Pool和Page Cache可能重复缓存(建议O_DIRECT绕过)
- 大量IO操作可能挤占Page Cache,影响其他应用
监控:
1 | # 查看Page Cache使用情况 |
32. 🔴 什么是OOM Killer?如何避免关键进程被Kill?
答:当系统内存不足时,Linux内核的OOM Killer会选择一个进程杀掉。
选择策略:
- 根据oom_score评分,分数越高越容易被杀
- 评分因素:内存使用量、进程优先级、运行时间
保护关键进程:
1 | # 查看进程的oom_score |
五、DNS与网络架构(36-45题)
33. 🔵 DNS解析的完整过程?
答:
1 | 浏览器缓存 → 操作系统缓存 → hosts文件 |
DNS记录类型:
- A记录:域名→IPv4
- AAAA记录:域名→IPv6
- CNAME记录:域名→另一个域名(别名)
- MX记录:邮件服务器
- TXT记录:文本信息(SPF、DKIM验证)
- SRV记录:服务发现
34. 🔴 CDN的工作原理?如何选择CDN策略?
答:CDN(Content Delivery Network)将内容缓存到离用户最近的节点。
工作流程:
1 | 用户请求 → DNS解析 → CDN智能DNS返回最近节点IP |
缓存策略:
- 静态资源:长缓存(CSS/JS/图片,Cache-Control: max-age=31536000)
- 动态内容:短缓存或不缓存
- 个性化内容:不缓存(或ESI边缘包含)
35. 🔴 什么是BGP?为什么多机房架构需要了解BGP?
答:BGP(Border Gateway Protocol)是互联网的路由协议。
与架构的关系:
- 多机房部署时,需要BGP实现流量调度
- Anycast:多个机房宣告同一IP,用户自动路由到最近的机房
- 故障切换:某机房故障时,BGP自动将流量切换到其他机房
36. 🔴 正向代理和反向代理的区别?
答:
正向代理:代理客户端,客户端知道代理的存在
- 场景:VPN、科学上网、企业出口代理
反向代理:代理服务端,客户端不知道代理的存在
- 场景:Nginx负载均衡、CDN、API网关
37. 🔴 负载均衡有哪些算法?各自适用什么场景?
答:
| 算法 | 原理 | 适用场景 |
|---|---|---|
| 轮询 | 依次分配 | 服务器性能相同 |
| 加权轮询 | 按权重分配 | 服务器性能不同 |
| 随机 | 随机选择 | 简单场景 |
| 最少连接 | 选择连接数最少的 | 长连接场景 |
| IP哈希 | 按客户端IP哈希 | 需要会话保持 |
| 一致性哈希 | 哈希环 | 缓存场景 |
四层 vs 七层负载均衡:
- 四层(L4):基于IP+端口转发,性能高(LVS、F5)
- 七层(L7):基于HTTP内容转发,功能丰富(Nginx、HAProxy)
六、综合实战(38-45题)
38. ⚫ 一个HTTP请求从浏览器到服务器的完整过程?
答:这是一道经典的综合题。
1 | 1. URL解析:解析协议、域名、端口、路径 |
39. ⚫ 如何设计一个高性能的网络通信框架?
答:参考Netty的设计。
核心设计:
- IO模型:epoll(Linux)/ kqueue(macOS)
- 线程模型:主从Reactor
- 内存管理:池化ByteBuf,减少GC
- 零拷贝:CompositeByteBuf、FileRegion
- 编解码:Pipeline责任链模式
- 背压:写缓冲区水位线控制
40. ⚫ 从网络角度,如何优化微服务间的通信延迟?
答:
- 连接复用:HTTP/2多路复用、gRPC长连接
- 协议优化:Protobuf替代JSON(体积小、解析快)
- 就近访问:同机房优先、同可用区优先
- 批量请求:合并多个小请求为一个批量请求
- 异步调用:非关键路径异步化
- 缓存:减少不必要的远程调用
- 内核调优:TCP参数优化、开启BBR