1. 协议栈基础:从物理信号到HTTP请求
做高并发服务,第一关就是协议栈。很多人上来就调线程池、调连接池,结果瓶颈其实在协议栈底层。我见过太多案例——应用层代码写得再漂亮,协议栈一堵,全白搭。
今天咱们先把地基打牢。协议栈这东西,说白了就是一套规则,让数据能从一台机器跑到另一台机器,还能被正确理解。
1.1 OSI七层模型 vs TCP/IP四层模型
先看这张对比图,心里有个全局印象。
OSI七层是教科书上的理想模型,分得特别细。但实际互联网跑的是TCP/IP四层模型——它把上面三层(应用、表示、会话)合并成一层,下面两层(物理、数据链路)也合并了。
我个人习惯直接拿TCP/IP模型来思考问题。你想想看,做高并发服务,真正需要操心的就三层:应用层、传输层、网络层。网络接口层一般交给内核和网卡驱动去管,我们很少直接碰。
1.2 核心协议的角色定位
三个核心协议,各有各的脾气。我列个表,方便你对比。
| 协议 | 层级 | 核心特点 | 典型场景 | 并发瓶颈 |
|---|---|---|---|---|
| TCP | 传输层 | 面向连接、可靠、有序、流量控制、拥塞控制 | HTTP、数据库连接、RPC | 三次握手开销、队头阻塞、TIME_WAIT |
| UDP | 传输层 | 无连接、不可靠、低延迟、无拥塞控制 | DNS、视频流、游戏、QUIC | 无内置拥塞控制,应用层需自行处理 |
| HTTP | 应用层 | 请求-响应模型、无状态、可扩展 | Web服务、API、微服务 | 短连接开销、头部冗余、队头阻塞 |
TCP:可靠但沉重
TCP是个老实人。它保证你发的每个字节都能按顺序到达。代价呢?三次握手、四次挥手、拥塞窗口、滑动窗口……每一个机制都在消耗时间和资源。
我记得有一次排查线上问题,发现服务端TIME_WAIT连接堆到十几万,新连接就是建不起来。查了半天,原来是客户端频繁创建短连接,服务端被动关闭后TIME_WAIT堆积。后来改成连接池,问题就解决了。
UDP:轻量但需要自己兜底
UDP就简单多了。它只管把数据包扔出去,不管对方收没收到。延迟低,开销小,但丢包、乱序都得应用层自己处理。
为什么QUIC(基于UDP的HTTP/3)越来越火?说白了就是受够了TCP的队头阻塞。我最近在做一个实时推送服务,底层就用了UDP+应用层重传,延迟比TCP方案低了40%。
HTTP:应用层的门面
HTTP是应用层最常用的协议。从HTTP/1.0的短连接,到HTTP/1.1的keep-alive,再到HTTP/2的多路复用,最后到HTTP/3的QUIC——每一次演进都在解决并发场景下的瓶颈。
你想想看,HTTP/1.1虽然支持了长连接,但同一个连接上的请求必须排队(队头阻塞)。HTTP/2引入了多路复用,但TCP层面的队头阻塞依然存在。直到HTTP/3换成UDP+QUIC,才算真正解决了这个问题。
1.3 并发场景下的协议栈瓶颈分析
高并发场景下,协议栈的瓶颈往往不在应用层,而在传输层和网络层。我总结了几类最常见的瓶颈,你看看自己遇到过几个。
1.3.1 连接建立与释放的开销
TCP三次握手需要1.5个RTT(往返时间)。如果客户端和服务端距离远,比如跨机房、跨地域,这个开销就很可观了。
举个例子:北京到上海的网络延迟大约30ms。一次三次握手就是45ms。如果每次请求都新建连接,光握手就占了45ms。QPI一高,连接建立本身就成了瓶颈。
1.3.2 队头阻塞(Head-of-Line Blocking)
队头阻塞是TCP协议的一个固有缺陷。TCP保证数据按序到达,但如果一个包丢了,后面的包都得等着。即使后面的包已经到达了接收端,也不能交给应用层处理。
HTTP/2虽然实现了多路复用,但底层还是TCP。一个TCP连接上的所有流共享一个拥塞窗口,一旦丢包,所有流都得等。这就是为什么HTTP/3要换成QUIC——每个流独立拥塞控制,一个流丢包不影响其他流。
1.3.3 缓冲区与内存开销
每个TCP连接都有发送缓冲区和接收缓冲区。默认值通常是16KB-64KB。如果服务端维持10万个连接,光缓冲区就要占掉几GB内存。
我记得有一次帮一个朋友优化网关,发现内存占用高得离谱。一查,TCP读写缓冲区都设成了默认的64KB。10万连接就是6.4GB。后来根据业务包大小,把缓冲区调到了4KB,内存直接降到400MB。
1.3.4 中断与上下文切换
高并发下,网卡中断和上下文切换是隐藏的杀手。每个数据包到达都会触发中断,中断处理程序把数据从网卡拷贝到内核缓冲区,然后通知应用层读取。这个过程涉及多次上下文切换。
现代网卡支持RSS(Receive Side Scaling)和多队列,可以把中断分散到多个CPU核心上。但配置不当的话,所有中断都挤在一个核心上,那个核心的CPU使用率直接飙到100%。
核心思路:协议栈瓶颈的本质是资源竞争。连接数、缓冲区、中断、上下文切换——这些资源在高并发下都会成为瓶颈。解决思路无非两条:
- 减少开销:连接池、长连接、零拷贝、大页内存
- 分散压力:多队列网卡、多线程/多进程、IO多路复用(epoll/kqueue)
1.4 一张图总结本章核心
下面这张图把协议栈的层次、核心协议、以及并发瓶颈串起来了。建议你保存下来,后面每章都会用到。
好了,第一章就到这里。协议栈基础是后面所有章节的基石。TCP的握手优化、UDP的可靠性改造、HTTP/2的多路复用、epoll的IO模型——这些都会在后续章节逐一展开。
记住一句话:高并发不是靠堆机器堆出来的,是靠把每一层的瓶颈都抠干净抠出来的。