1. 确定优化目标

虽然网络性能优化的整体目标,是降低网络延迟(如 RTT)和提高吞吐量(如 BPS 和 PPS),但具体到不同应用中,每个指标的优化标准可能会不同,优先级顺序也大相径庭。

  • 网络接口层和网络层。主要负责网络包的封装、寻址、路由,以及发送和接收。
    • 指标:每秒可处理的网络包数PPS
    • 工具:pktgen
  • 传输层TCP和UDP。负责网络传输。
    • 指标:吞吐量 (BPS)、连接数以及延迟
    • 工具: iperf 或 netperf
  • 应用层
    • 指标:吞吐量(BPS)、每秒请求数、延迟
    • 工具:wrk、ab

2. 网络性能工具

指标查工具 工具查指标

3. 网络性能优化

3.1 应用程序

1. C10K和100K问题

  • 从网络I/O角度,2种优化思路:
    • 最常用的 I/O 多路复用技术 epoll,取代poll和select
    • 使用异步 I/O。等到 I/O 完成后,系统会用事件通知的方式,告诉 应用程序结果
  • 从进程的工作模型角度,2种优化思路:
    • 主进程 + 多个worker子进程。(其中,主进程负责管理网络连接,而子进程负责实际的业务处理。)
    • 监听到相同端口的多进程模型。(所有进程都会监听相同接口,并且开启SO_REUSEPORT选项,由内核负责,把请求负载均衡到这些监听进程中去。)
  • 应用层的网络协议优化
    • 使用长连接取代短连接,可以显著降低 TCP 建立连接的成本。
    • 使用内存等方式,来缓存不常变化的数据,可以降低网络 I/O 次数
    • Protocol Buffer 等序列化的方式,压缩网络 I/O 的数据量,可以提高应用程序的 吞吐。
    • 使用 DNS 缓存、预取、HTTPDNS 等方式,减少 DNS 解析的延迟,也可以提升网络 I/O 的整体速度。

3.2 套接字

套接字可以屏蔽掉 Linux 内核中不同协议的差异,为应用程序提供统一的访问接口。每个套接字,都有一个读写缓冲区。

  • 读缓冲区,缓存了远端发过来的数据。如果读缓冲区已满,就不能再接收新的数据。
  • 写缓冲区,缓存了要发出去的数据。如果写缓冲区已满,应用程序的写操作就会被阻塞。

为了提高网络的吞吐量,你通常需要调整这些缓冲区的大小

3.3 传输层

传输层最重要的是TCP和UDP协议,其实主要就是对这两种协议的优化。

第一类,在请求数比较大的场景下,你可能会看到大量处于 TIME_WAIT 状态的连接,它 们会占用大量内存和端口资源。

  • 增大处于 TIME_WAIT 状态的连接数量 net.ipv4.tcp_max_tw_buckets ,并增大连接跟 踪表的大小 net.netfilter.nf_conntrack_max。
  • 减小 net.ipv4.tcp_fin_timeout 和 net.netfilter.nf_conntrack_tcp_timeout_time_wait ,让系统尽快释放它们所占用的资源。
  • 开启端口复用 net.ipv4.tcp_tw_reuse。这样,被 TIME_WAIT 状态占用的端口,还能用 到新建的连接中。
  • 增大本地端口的范围 net.ipv4.ip_local_port_range。提高并发
  • 增加最大文件描述符的数量

第二类,为了缓解 SYN FLOOD 等,利用 TCP 协议特点进行攻击而引发的性能问题,你 可以考虑优化与 SYN 状态相关的内核选项

  • 增大 TCP 半连接的最大数量 net.ipv4.tcp_max_syn_backlog ,或者开启 TCP SYN Cookies net.ipv4.tcp_syncookies ,来绕开半连接数量限制的问题
  • 减少 SYN_RECV 状态的连接重传 SYN+ACK 包的次数 net.ipv4.tcp_synack_retries。

第三类,在长连接的场景中,通常使用 Keepalive 来检测 TCP 连接的状态,以便对端连 接断开后,可以自动回收。系统默认的 Keepalive 探测间隔和重试次数,一般都无 法满足应用程序的性能要求。所以,这时候你需要优化与 Keepalive 相关的内核选项

  • 缩短最后一次数据包到 Keepalive 探测包的间隔时间 net.ipv4.tcp_keepalive_time;
  • 缩短发送 Keepalive 探测包的间隔时间 net.ipv4.tcp_keepalive_intvl;
  • 减少 Keepalive 探测失败后,一直到通知应用程序前的重试次数 net.ipv4.tcp_keepalive_probes。

3.4 网络层

网络层,负责网络包的封装、寻址和路由,包括IP、ICMP等常见协议。在网络层,最主要的优化,其实就是对路由、 IP分片以及ICMP等进行调优。

第一种,从路由和转发的角度出发

  • 在需要转发的服务器中,比如用作 NAT 网关的服务器或者使用 Docker 容器时,开启 IP 转发,即设置 net.ipv4.ip_forward = 1。
  • 调整数据包的生存周期 TTL,比如设置 net.ipv4.ip_default_ttl = 64
  • 开启数据包的反向地址校验,比如设置 net.ipv4.conf.eth0.rp_filter = 1。这样可以防 止 IP 欺骗,并减少伪造 IP 带来的 DDoS 问题。

第二种,从分片的角度出发,最主要的是调整 MTU(Maximum Transmission Unit)的 大小。 第三种,从 ICMP 的角度出发,为了避免 ICMP 主机探测、ICMP Flood 等各种网络问 题,你可以通过内核选项,来限制 ICMP 的行为。

3.5 链路层

链路层负责网络包在物理网络中的传输,比如 MAC 寻址、错误侦测以及通过网卡传输网 络帧等。自然,链路层的优化,也是围绕这些基本功能进行的。

主要是优化网络包的收发、网络功能卸载以及网卡选项。