网络与操作系统基础

网络和操作系统是架构师排查线上问题的底层功力。本模块覆盖TCP/IP、HTTP/HTTPS、IO模型、Linux内核调优等核心知识点,共60题。


难度标记

  • 🔵 高级(Senior):8-10年经验应该能答好
  • 🔴 专家(Expert):需要深入的实战经验
  • ⚫ 大师(Master):开放性问题

一、TCP/IP协议(1-20题)

1. 🔵 TCP三次握手的过程和每一步的作用?

答:三次握手建立可靠连接。

1
2
3
4
客户端              服务端
|--- SYN(seq=x) --->| 第1次:客户端请求建立连接
|<-- SYN+ACK(seq=y, ack=x+1) --| 第2次:服务端确认并请求建立反向连接
|--- ACK(ack=y+1) -->| 第3次:客户端确认反向连接

为什么是三次而不是两次:

  • 两次握手无法防止历史连接的建立
  • 场景:客户端发送的旧SYN延迟到达,服务端误以为是新连接
  • 三次握手时客户端会发现序列号不对,发送RST终止

为什么不是四次:

  • 三次已经足够双方确认各自的发送和接收能力
  • 第二步合并了SYN和ACK,减少一次交互

2. 🔵 TCP四次挥手的过程?为什么需要TIME_WAIT?

答:四次挥手关闭连接。

1
2
3
4
5
6
客户端              服务端
|--- FIN --->| 第1次:客户端请求关闭
|<-- ACK ----| 第2次:服务端确认(可能还有数据要发)
|<-- FIN ----| 第3次:服务端也请求关闭
|--- ACK --->| 第4次:客户端确认
| TIME_WAIT | 等待2MSL后关闭

TIME_WAIT的作用:

  1. 确保最后一个ACK能到达服务端(如果丢失,服务端会重发FIN)
  2. 让本连接的所有报文在网络中消失,防止影响新连接

TIME_WAIT过多的问题:

  • 占用端口和内存
  • 解决方案:net.ipv4.tcp_tw_reuse=1(允许复用TIME_WAIT连接)
  • net.ipv4.tcp_tw_recycle=1(快速回收,NAT环境下有问题,不推荐)

3. 🔴 TCP的拥塞控制算法有哪些?实际生产中如何选择?

答:拥塞控制是TCP性能的核心。

经典算法:

  1. 慢启动:cwnd从1开始指数增长,到达ssthresh后进入拥塞避免
  2. 拥塞避免:cwnd线性增长
  3. 快重传:收到3个重复ACK立即重传
  4. 快恢复:快重传后cwnd减半,进入拥塞避免(不回到慢启动)

现代算法:

  • CUBIC:Linux默认,基于三次函数调整cwnd,对高延迟网络友好
  • BBR:Google开发,基于带宽和RTT估算,不依赖丢包信号
    • 优势:在有一定丢包率的网络中表现远好于CUBIC
    • 适合:长肥管道(高带宽高延迟)、有一定丢包的网络
    • 开启:sysctl net.ipv4.tcp_congestion_control=bbr

生产建议:

  • 内网通信:CUBIC(默认即可)
  • 跨机房/跨国:BBR(显著提升吞吐量)
  • CDN/直播:BBR

4. 🔴 TCP粘包和拆包是什么?如何解决?

答:TCP是字节流协议,没有消息边界的概念。

粘包:多个消息被合并成一个TCP段发送
拆包:一个消息被拆分成多个TCP段发送

解决方案:

  1. 固定长度:每个消息固定N字节(简单但浪费)
  2. 分隔符:用特殊字符分隔消息(如HTTP的\r\n)
  3. 长度前缀:消息头包含消息体长度(最常用)
    1
    [4字节长度][消息体]
  4. 自定义协议:如Dubbo协议、Redis协议

Netty中的解决方案:

  • FixedLengthFrameDecoder:固定长度
  • DelimiterBasedFrameDecoder:分隔符
  • LengthFieldBasedFrameDecoder:长度前缀(推荐)

5. 🔴 TCP的Keep-Alive和应用层心跳有什么区别?

答:两者都是检测连接存活,但层次和目的不同。

TCP Keep-Alive:

  • 操作系统层面,默认2小时发一次探测包
  • 参数:tcp_keepalive_time=7200tcp_keepalive_intvl=75tcp_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的优势:

  1. 0-RTT建连:首次1-RTT,后续0-RTT(TCP+TLS需要3-RTT)
  2. 多路复用无队头阻塞:HTTP/2的多路复用在TCP层仍有队头阻塞,QUIC在UDP上实现独立的流
  3. 连接迁移:基于Connection ID而非四元组,WiFi切4G不断连
  4. 内置加密:TLS 1.3集成在协议中
  5. 改进的拥塞控制:可以在用户空间快速迭代

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. 应用代码没有正确关闭连接(最常见)
  2. 连接池配置不当(连接泄漏)
  3. 应用处理慢,来不及关闭

排查步骤:

1
2
3
4
5
6
7
8
9
10
11
# 查看CLOSE_WAIT连接数
netstat -ant | grep CLOSE_WAIT | wc -l

# 查看哪个进程持有这些连接
netstat -antp | grep CLOSE_WAIT

# 查看进程的文件描述符
ls -la /proc/<pid>/fd | wc -l

# 用lsof查看具体连接
lsof -p <pid> | grep CLOSE_WAIT

解决方案:

  • 检查代码中HTTP连接、数据库连接是否正确关闭(try-with-resources)
  • 检查连接池配置(最大连接数、空闲超时)
  • 设置SO_LINGER选项强制关闭

11. 🔴 线上遇到大量TIME_WAIT怎么处理?

答:TIME_WAIT过多通常出现在短连接高并发场景。

原因:主动关闭连接的一方会进入TIME_WAIT,持续2MSL(通常60秒)

解决方案:

  1. 连接复用:使用长连接/连接池,减少连接创建和关闭
  2. 内核参数调优
    1
    2
    3
    net.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超时
  3. 让服务端主动关闭:调整为客户端主动关闭(TIME_WAIT在客户端)
  4. 使用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
2
3
4
5
6
7
8
9
10
客户端                          服务端
|--- ClientHello(支持的加密套件、随机数)--->|
|<-- ServerHello(选择的加密套件、随机数)---|
|<-- Certificate(服务端证书)------------|
|<-- ServerHelloDone ------------------|
|--- ClientKeyExchange(预主密钥)------->|
|--- ChangeCipherSpec ----------------->|
|--- Finished ------------------------>|
|<-- ChangeCipherSpec ------------------|
|<-- Finished --------------------------|

TLS 1.3握手(1-RTT):

  • 合并了多个步骤,只需1个RTT
  • 移除了不安全的加密算法
  • 支持0-RTT恢复(用之前的密钥直接发送数据)

14. 🔴 HTTPS的证书链验证过程?

答:证书链是信任传递的机制。

1
2
3
根CA证书(预装在操作系统/浏览器中)
└── 中间CA证书(由根CA签发)
└── 服务端证书(由中间CA签发)

验证过程:

  1. 服务端发送自己的证书和中间CA证书
  2. 客户端用中间CA的公钥验证服务端证书的签名
  3. 客户端用根CA的公钥验证中间CA证书的签名
  4. 根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
2
3
4
请求 → 检查强缓存 → 未过期:直接使用(200 from cache)
→ 已过期:发送请求(携带If-None-Match/If-Modified-Since)
→ 服务端验证 → 未修改:304(使用缓存)
→ 已修改:200(返回新内容)

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
2
3
4
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400 # 预检结果缓存时间

三、IO模型与网络编程(21-35题)

21. 🔵 Linux的五种IO模型是什么?

答:

  1. 阻塞IO(Blocking IO):read()阻塞直到数据就绪
  2. 非阻塞IO(Non-blocking IO):read()立即返回,需要轮询
  3. IO多路复用(IO Multiplexing):select/poll/epoll监听多个fd
  4. 信号驱动IO(Signal-driven IO):注册信号处理函数,数据就绪时通知
  5. 异步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高性能的原因:

  1. 红黑树管理fd:增删改查O(log n)
  2. 就绪链表:只返回就绪的fd,不需要遍历所有
  3. mmap:内核和用户空间共享内存,减少拷贝
  4. 边缘触发(ET):只在状态变化时通知,减少系统调用

epoll的两种模式:

  • LT(水平触发):只要fd就绪就一直通知(默认,编程简单)
  • ET(边缘触发):只在状态变化时通知一次(性能更好,编程复杂)

23. 🔴 Reactor模式是什么?Netty的线程模型?

答:Reactor是高性能网络编程的核心模式。

单Reactor单线程:

  • 一个线程处理所有事件(接受连接、读写、业务处理)
  • 适合:业务处理快的场景(如Redis)

单Reactor多线程:

  • 一个线程处理连接和IO事件
  • 业务处理交给线程池
  • 问题:单Reactor成为瓶颈

主从Reactor多线程(Netty默认):

1
2
3
MainReactor(BossGroup):接受连接,将新连接注册到SubReactor
SubReactor(WorkerGroup):处理IO读写事件
业务线程池:处理业务逻辑(可选)

Netty线程模型:

1
2
EventLoopGroup bossGroup = new NioEventLoopGroup(1);    // 1个线程接受连接
EventLoopGroup workerGroup = new NioEventLoopGroup(8); // 8个线程处理IO

24. 🔴 什么是零拷贝(Zero Copy)?有哪些实现方式?

答:零拷贝减少数据在内核空间和用户空间之间的拷贝次数。

传统IO(4次拷贝):

1
磁盘 → 内核缓冲区 → 用户缓冲区 → Socket缓冲区 → 网卡

零拷贝方案:

  1. mmap + write(3次拷贝):

    • mmap将内核缓冲区映射到用户空间
    • 减少一次内核→用户的拷贝
  2. sendfile(2次拷贝,Linux 2.4+):

    1
    磁盘 → 内核缓冲区 → 网卡(DMA直接传输)
    • 数据不经过用户空间
    • Java中:FileChannel.transferTo()
  3. splice(Linux 2.6.17+):

    • 在两个fd之间直接传输数据
    • 不需要内核缓冲区到Socket缓冲区的拷贝

应用场景:

  • Kafka:使用sendfile发送消息给消费者
  • Nginx:使用sendfile发送静态文件
  • Netty:使用FileRegion封装零拷贝

25. 🔴 Kafka为什么性能这么高?从网络和IO角度分析。

答:Kafka的高性能是多种技术的组合。

  1. 顺序写磁盘:追加写入,利用磁盘顺序IO(比随机IO快1000倍)
  2. Page Cache:利用操作系统的页缓存,减少磁盘IO
  3. 零拷贝:sendfile直接从Page Cache发送到网卡
  4. 批量处理:消息批量发送和消费,减少网络往返
  5. 压缩:消息批量压缩,减少网络传输量
  6. 分区并行:多分区并行读写

四、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
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 找到CPU最高的进程
top -c

# 2. 找到CPU最高的线程
top -Hp <pid>

# 3. 将线程ID转为16进制
printf "%x\n" <tid>

# 4. 用jstack查看线程堆栈
jstack <pid> | grep <tid_hex> -A 30

# 5. 或者用arthas一步到位
thread -n 3 # 查看CPU最高的3个线程

常见原因:

  1. 死循环:代码bug导致无限循环
  2. 频繁GC:Full GC导致CPU飙高(jstat -gcutil <pid> 1000
  3. 正则回溯:复杂正则表达式导致灾难性回溯
  4. 加密/压缩:大量加密或压缩操作
  5. 线程竞争:大量线程竞争锁,自旋消耗CPU

28. 🔴 内存泄漏怎么排查?

答:Java内存泄漏排查流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 查看内存使用趋势
jstat -gcutil <pid> 1000

# 2. 如果Old区持续增长且Full GC后不释放,可能有泄漏

# 3. dump堆内存
jmap -dump:format=b,file=heap.hprof <pid>
# 或者OOM时自动dump
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/

# 4. 用MAT(Memory Analyzer Tool)分析
# - Leak Suspects Report:自动分析可疑泄漏点
# - Dominator Tree:查看占用内存最大的对象
# - Histogram:查看各类对象的数量和大小

# 5. 用arthas在线分析
heapdump /tmp/heap.hprof

常见泄漏原因:

  1. 集合类未清理:静态Map/List持续添加不删除
  2. 连接未关闭:数据库连接、HTTP连接泄漏
  3. ThreadLocal未清理:线程池中ThreadLocal不remove
  4. 监听器未注销:注册了事件监听器但未注销
  5. 缓存无上限:本地缓存没有设置最大容量和过期时间

29. 🔴 Linux的文件描述符(fd)是什么?fd耗尽怎么处理?

答:fd是Linux中对打开文件、Socket、管道等的抽象引用。

1
2
3
4
5
6
7
8
9
10
11
# 查看系统级fd限制
cat /proc/sys/fs/file-max

# 查看进程级fd限制
ulimit -n

# 查看进程已使用的fd数
ls /proc/<pid>/fd | wc -l

# 查看系统已使用的fd数
cat /proc/sys/fs/file-nr

fd耗尽的原因:

  1. 连接泄漏(Socket未关闭)
  2. 文件未关闭
  3. ulimit设置太小

解决方案:

1
2
3
4
5
6
7
8
9
# 临时调整
ulimit -n 65535

# 永久调整 /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535

# 系统级调整
sysctl -w fs.file-max=1000000

30. 🔴 Linux内核参数调优有哪些关键参数?

答:高并发服务器必须调优的参数。

网络相关:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 最大连接数
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# 端口范围
net.ipv4.ip_local_port_range = 1024 65535

# TIME_WAIT优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_tw_buckets = 5000

# TCP缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# 开启BBR
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

内存相关:

1
2
3
4
5
# 减少swap使用
vm.swappiness = 10

# OOM Killer调整
vm.overcommit_memory = 0

文件系统:

1
2
3
# 文件描述符
fs.file-max = 1000000
fs.nr_open = 1000000

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
2
3
4
5
# 查看Page Cache使用情况
free -h # cached列

# 查看缓存命中率
perf stat -e cache-misses,cache-references -p <pid>

32. 🔴 什么是OOM Killer?如何避免关键进程被Kill?

答:当系统内存不足时,Linux内核的OOM Killer会选择一个进程杀掉。

选择策略:

  • 根据oom_score评分,分数越高越容易被杀
  • 评分因素:内存使用量、进程优先级、运行时间

保护关键进程:

1
2
3
4
5
6
7
8
9
# 查看进程的oom_score
cat /proc/<pid>/oom_score

# 设置oom_score_adj(-1000到1000,-1000表示永不杀)
echo -1000 > /proc/<pid>/oom_score_adj

# 或者在systemd中配置
[Service]
OOMScoreAdjust=-1000

五、DNS与网络架构(36-45题)

33. 🔵 DNS解析的完整过程?

答:

1
2
3
4
浏览器缓存 → 操作系统缓存 → hosts文件
→ 本地DNS服务器(递归查询)
→ 根DNS服务器 → .com顶级DNS → example.com权威DNS
← 返回IP地址(缓存TTL时间)

DNS记录类型:

  • A记录:域名→IPv4
  • AAAA记录:域名→IPv6
  • CNAME记录:域名→另一个域名(别名)
  • MX记录:邮件服务器
  • TXT记录:文本信息(SPF、DKIM验证)
  • SRV记录:服务发现

34. 🔴 CDN的工作原理?如何选择CDN策略?

答:CDN(Content Delivery Network)将内容缓存到离用户最近的节点。

工作流程:

1
2
3
4
用户请求 → DNS解析 → CDN智能DNS返回最近节点IP
→ 请求到达CDN边缘节点
→ 缓存命中:直接返回
→ 缓存未命中:回源到源站获取,缓存后返回

缓存策略:

  • 静态资源:长缓存(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
2
3
4
5
6
7
8
9
10
11
12
13
14
1. URL解析:解析协议、域名、端口、路径
2. DNS解析:域名→IP地址
3. TCP三次握手:建立连接
4. TLS握手:(如果是HTTPS)建立加密通道
5. 发送HTTP请求:请求行+请求头+请求体
6. 服务端处理:
→ Nginx接收请求
→ 反向代理到后端服务
→ 后端处理业务逻辑
→ 查询数据库/缓存
→ 返回响应
7. 接收HTTP响应:状态行+响应头+响应体
8. 浏览器渲染:解析HTML→构建DOM→加载CSS/JS→渲染页面
9. TCP四次挥手:(如果Connection: close)关闭连接

39. ⚫ 如何设计一个高性能的网络通信框架?

答:参考Netty的设计。

核心设计:

  1. IO模型:epoll(Linux)/ kqueue(macOS)
  2. 线程模型:主从Reactor
  3. 内存管理:池化ByteBuf,减少GC
  4. 零拷贝:CompositeByteBuf、FileRegion
  5. 编解码:Pipeline责任链模式
  6. 背压:写缓冲区水位线控制

40. ⚫ 从网络角度,如何优化微服务间的通信延迟?

答:

  1. 连接复用:HTTP/2多路复用、gRPC长连接
  2. 协议优化:Protobuf替代JSON(体积小、解析快)
  3. 就近访问:同机房优先、同可用区优先
  4. 批量请求:合并多个小请求为一个批量请求
  5. 异步调用:非关键路径异步化
  6. 缓存:减少不必要的远程调用
  7. 内核调优:TCP参数优化、开启BBR