TCP 三次握手的性能提升
客户端的优化
当客户端发起 SYN 包时,可以通过 tcp_syn_retries 控制其重传的次数。
服务端的优化
当服务端 SYN 半连接队列溢出后,会导致后续连接被丢弃,可以通过 netstat -s 观察半连接队列溢出的情况,如果 SYN 半连接队列溢出情况比较严重,可以通过 tcp_max_syn_backlog、somaxconn、backlog 参数来调整 SYN 半连接队列的大小。
服务端回复 SYN+ACK 的重传次数由 tcp_synack_retries 参数控制。如果遭受 SYN 攻击,应把 tcp_syncookies 参数设置为 1,表示仅在 SYN 队列满后开启 syncookie 功能,可以保证正常的连接成功建立。
服务端收到客户端返回的 ACK,会把连接移入 accpet 队列,等待进行调用 accpet() 函数取出连接。
可以通过 ss -lnt 查看服务端进程的 accept 队列长度,如果 accept 队列溢出,系统默认丢弃 ACK,如果可以把 tcp_abort_on_overflow 设置为 1 ,表示用 RST 通知客户端连接建立失败。
如果 accpet 队列溢出严重,可以通过 listen 函数的 backlog 参数和 somaxconn 系统参数提高队列大小,accept 队列长度取决于 min(backlog, somaxconn)。
绕过三次握手
TCP Fast Open 功能可以绕过三次握手,使得 HTTP 请求减少了 1 个 RTT 的时间,Linux 下可以通过 tcp_fastopen 开启该功能,同时必须保证服务端和客户端同时支持。
TCP 四次挥手的性能提升
主动方的优化
主动发起 FIN 报文断开连接的一方,如果迟迟没收到对方的 ACK 回复,则会重传 FIN 报文,重传的次数由 tcp_orphan_retries 参数决定。
当主动方收到 ACK 报文后,连接就进入 FIN_WAIT2 状态,根据关闭的方式不同,优化的方式也不同:
:如果这是 close 函数关闭的连接,那么它就是孤儿连接。如果 tcp_fin_timeout 秒内没有收到对方的 FIN 报文,连接就直接关闭。同时,为了应对孤儿连接占用太多的资源,tcp_max_orphans 定义了最大孤儿连接的数量,超过时连接就会直接释放。
:反之是 shutdown 函数关闭的连接,则不受此参数限制;当主动方接收到 FIN 报文,并返回 ACK 后,主动方的连接进入 TIME_WAIT 状态。这一状态会持续 1 分钟,为了防止 TIME_WAIT 状态占用太多的资源,tcp_max_tw_buckets 定义了最大数量,超过时连接也会直接释放。
当 TIME_WAIT 状态过多时,还可以通过设置 tcp_tw_reuse 和 tcp_timestamps 为 1 ,将 TIME_WAIT 状态的端口复用于作为客户端的新连接,注意该参数只适用于客户端。
被动方的优化
被动关闭的连接方应对非常简单,它在回复 ACK 后就进入了 CLOSE_WAIT 状态,等待进程调用 close 函数关闭连接。因此,出现大量 CLOSE_WAIT 状态的连接时,应当从应用程序中找问题。
当被动方发送 FIN 报文后,连接就进入 LAST_ACK 状态,在未等到 ACK 时,会在 tcp_orphan_retries 参数的控制下重发 FIN 报文。
TCP 传输数据的性能提升
TCP 可靠性是通过 ACK 确认报文实现的,又依赖滑动窗口提升了发送速度也兼顾了接收方的处理能力。可是,默认的滑动窗口最大值只有 64 KB,不满足当今的高速网络的要求,要想提升发送速度必须提升滑动窗口的上限,在 Linux 下是通过设置 tcp_window_scaling 为 1 做到的,此时最大值可高达 1GB。
滑动窗口定义了网络中飞行报文的最大字节数,当它超过带宽时延积时,网络过载,就会发生丢包。而当它小于带宽时延积时,就无法充分利用网络带宽。因此,滑动窗口的设置,必须参考带宽时延积
内核缓冲区决定了滑动窗口的上限,缓冲区可分为:发送缓冲区 tcp_wmem 和接收缓冲区 tcp_rmem。
Linux 会对缓冲区动态调节,我们应该把缓冲区的上限设置为带宽时延积。发送缓冲区的调节功能是自动打开的,而接收缓冲区需要把 tcp_moderate_rcvbuf 设置为 1 来开启。其中,调节的依据是 TCP 内存范围 tcp_mem。
但需要注意的是,如果程序中的 socket 设置 SO_SNDBUF 和 SO_RCVBUF,则会关闭缓冲区的动态整功能,所以不建议在程序设置它俩,而是交给内核自动调整比较好。
有效配置这些参数后,既能够最大程度地保持并发性,也能让资源充裕时连接传输速度达到最大值。