您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 
 订阅
【linux性能优化】网络性能优化的思路
 
作者:sysu_lluozh
  1289  次浏览      17 次
 2022-2-11
 
编辑推荐:
本文主要介绍从应用程序、套接字、传输层、网络层,再到链路层,分别介绍了相应的网络性能优化方法 。
本文来自于CSDN ,由火龙果软件Alice编辑、推荐。

网络问题比CPU、内存或磁盘I/O都要复杂,无论是应用层的各种I/O模型,冗长的网络协议栈和众多的内核选项,抑或是各种复杂的网络环境都提高了网络的复杂性

掌握Linux网络的基本原理和常见网络协议的工作流程, 再结合各个网络层的性能指标来分析,其实定位网络瓶颈并不难

找到网络性能瓶颈后,下一步要做的就是优化了,也就是如何降低网络延迟并提高网络的吞吐量

一、确定优化目标

跟CPU和I/O方面的性能优化一样,优化前首先要确认网络性能优化的目标是什么?换句话说,观察到的网络性能指标达到多少才合适呢?

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

为了更客观合理地评估优化效果,首先应该明确优化的标准,即要对系统和应用程序进行基准测试,得到网络协议栈各层的基准性能

Linux网络协议栈是需要掌握的核心原理,它是基于TCP/IP协议族的分层结构,用一张图来表示这个结构:

在进行基准测试时可以按照协议栈的每一层来测试

由于底层是其上方各层的基础,底层性能也就决定了高层性能。所以要清楚底层性能指标其实就是对应高层的极限性能,接下来从下到上来理解这一点

网络接口层和网络层

首先是网络接口层和网络层,它们主要负责网络包的封装、寻址、路由,以及发送和接收

每秒可处理的网络包数PPS,就是它们最重要的性能指标(特别是在小包的情况下)

可以使用内核自带的发包工具pktgen来测试PPS的性能

传输层的TCP和UDP

再向上到传输层的TCP和UDP,它们主要负责网络传输

对它们而言,吞吐量(BPS)、 连接数以及延迟就是最重要的性能指标

可以用iperf或netperf来测试传输层的性能

注: 网络包的大小会直接影响这些指标的值,所以通常需要测试一系列不同大小网络包的性能

应用层

最后再往上到了应用层,最需要关注的是吞吐量(BPS)、每秒请求数以及延迟等指标

可以用wrk、ab等工具来测试应用程序的性能

注:测试场景要尽量模拟生产环境这样的测试才更有价值,比如可以到生产环境中录制实际的请求情况再到测试中回放

总之,根据这些基准指标,再结合已经观察到的性能瓶颈就可以明确性能优化的目标

二、网络性能工具

可以从指标和工具两个不同维度出发,整理记忆网络相关的性能工具

2.1 从网络性能指标出发

第一个维度,从网络性能指标出发,更容易把性能工具同系统工作原理关联起来,对性能问题有宏观的认识和把握

这样,当想查看某个性能指标时,可以清楚知道可以用哪些工具

把提供网络性能指标的工具做成了一个表格如下:

2.2 从性能工具出发

第二个维度,从性能工具出发,可以更快上手使用工具迅速找出想要观察的性能指标

特别是在工具有限的情况下,可以更充分利用好手头的每一个工具,用少量工具也要尽力挖掘出大量信息

将这些常用工具汇总成了一个表格如下:

三、网络性能优化

总的来说,先要获得网络基准测试报告,然后通过相关性能工具定位出网络性能瓶颈,接下来才是优化工作

当然,要优化网络性能肯定离不开Linux系统的网络协议栈和网络收发流程的辅助

流程图如下:

接下来,从应用程序、套接字、传输层、网络层以及链路层等几个角度分别看网络性能优化的基本思路

3.1 应用程序

应用程序,通常通过套接字接口进行网络操作

由于网络收发通常比较耗时,所以应用程序的优化主要就是对网络I/O和进程自身的工作模型的优化

3.1.1 对于网络I/O

从网络I/O的角度主要有下面两种优化思路:

I/O多路复用技术epoll

第一种是最常用的I/O多路复用技术epoll,主要用来取代select和poll

这其实是解决C10K问题的关键,也是目前很多网络应用默认使用的机制

异步I/O

第二种是使用异步I/O(Asynchronous I/O,AIO)

AIO 允许应用程序同时发起很多I/O操作而不用等待这些操作完成,等到 I/O完成后系统会用事件通知的方式,告诉应用程序结果

不过,AIO的使用比较复杂,需要小心处理很多边缘情况

3.1.2 对于进程的工作模型

而从进程的工作模型来说,也有两种不同的模型用来优化

主进程+多个worker子进程

第一种,主进程+多个worker子进程

其中,主进程负责管理网络连接,而子进程负责实际的业务处理,这也是最常用的一种模型

监听到相同端口的多进程模型

第二种,监听到相同端口的多进程模型

在这种模型下,所有进程都会监听相同接口,并且开启SO_REUSEPORT选项,由内核负责把请求负载均衡到这些监听进程中去

3.1.3 应用层的网络协议优化

除了网络I/O和进程的工作模型外,应用层的网络协议优化也是至关重要的一点。常见的几种优化方法

使用长连接取代短连接

可以显著降低TCP建立连接的成本,在每秒请求次数较多时这样做的效果非常明显

使用内存存储不常变化的数据

使用内存等方式来缓存不常变化的数据,可以降低网络I/O次数,同时加快应用程序的响应速度

序列化压缩数据量

使用Protocol Buffer等序列化的方式,压缩网络I/O的数据量,提高应用程序的吞吐

DNS缓存

使用DNS缓存、预取、HTTPDNS等方式,减少DNS解析的延迟,提升网络I/O的整体速度

3.2 套接字

套接字可以屏蔽掉Linux内核中不同协议的差异,为应用程序提供统一的访问接口

每个套接字都有一个读写缓冲区 :

读缓冲区

缓存了远端发过来的数据,如果读缓冲区已满,就不能再接收新的数据

写缓冲区

缓存了要发出去的数据,如果写缓冲区已满,应用程序的写操作就会被阻塞

所以,为了提高网络的吞吐量,通常需要调整这些缓冲区的大小。比如:

增大每个套接字的缓冲区大小net.core.optmem_max

增大套接字接收缓冲区大小net.core.rmem_max和发送缓冲区大小net.core.wmem_max

增大TCP接收缓冲区大小net.ipv4.tcp_rmem和发送缓冲区大小net.ipv4.tcp_wmem

将套接字的内核选项整理成了一个表格如下:

不过有几点需要注意:

tcp_rmem和tcp_wmem

tcp_rmem和tcp_wmem的三个数值分别是min,default,max,系统会根据这些设置自动调整TCP接收/发送缓冲区的大小

udp_mem

udp_mem的三个数值分别是min,pressure,max,系统会根据这些设置自动调整UDP发送缓冲区的大小

当然,表格中的数值只提供参考价值,具体应该设置多少需要根据实际的网络状况来确定

比如,发送缓冲区大小理想数值是吞吐量*延迟,这样才可以达到最大网络利用率

除此之外,套接字接口还提供了一些配置选项,用来修改网络连接的行为:

为TCP连接设置TCP_NODELAY后,可以禁用Nagle算法

为TCP连接开启TCP_CORK后,可以让小包聚合成大包后再发送(注意会阻塞小包的发送)

使用SO_SNDBUF和SO_RCVBUF,可以分别调整套接字发送缓冲区和接收缓冲区的大小

3.3 传输层

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

3.3.1 TCP协议的优化

TCP 提供了面向连接的可靠传输服务

要优化TCP,首先要掌握TCP协议的基本原理,比如流量控制、慢启动、拥塞避免、延迟确认以及状态流图等,如下图所示:

不破坏TCP正常工作的基础上,对其的优化分几类情况详细说明:

减少TIME_WAIT 状态

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

这时,可以优化与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,这样可以支持更多连接,提高整体的并发能力

增加最大文件描述符的数量,可以使用fs.nr_open和fs.file-max分别增大进程和系统的最大文件描述符数,或在应用程序的systemd配置文件中配置LimitNOFILE,设置应用程序的最大文件描述符数

缓解SYN FLOOD

第二类,为了缓解SYN FLOOD等利用TCP协议特点进行攻击而引发的性能问题,可以考虑优化与SYN状态相关的内核选项,比如采取下面几种措施:

增大TCP半连接的最大数量net.ipv4.tcp_max_syn_backlog,或者开启TCP SYN Cookiesnet.ipv4.tcp_syncookies来绕开半连接数量限制的问题(注意,这两个选项不可同时使用)

减少SYN_RECV状态的连接重传SYN+ACK包的次数net.ipv4.tcp_synack_retries

优化Keepalive参数

第三类,在长连接的场景中通常使用Keepalive来检测TCP连接的状态,以便对端连接断开后可以自动回收

但是,系统默认的Keepalive探测间隔和重试次数一般都无法满足应用程序的性能要求。这时候需要优化与Keepalive相关的内核选项,比如:

缩短最后一次数据包到Keepalive探测包的间隔时间net.ipv4.tcp_keepalive_time

缩短发送Keepalive探测包的间隔时间net.ipv4.tcp_keepalive_intvl

减少Keepalive探测失败后,一直到通知应用程序前的重试次数net.ipv4.tcp_keepalive_probes

综合上面TCP 优化方法,整理成了一个表格方便在需要时参考(数值仅供

参考,具体配置结合实际场景来调整):

优化TCP性能时,要注意如果同时使用不同优化方法,可能会产生冲突。比如:

服务器端开启Nagle算法,而客户端开启延迟确认机制,很容易导致网络延迟增大

开启net.ipv4.tcp_tw_recycle很容易导致各种连接失败

3.3.2 UDP协议的优化

说完TCP,再来看UDP的优化

UDP提供面向数据报的网络协议,它不需要网络连接,也不提供可靠性保障。所以,UDP优化相对于TCP来说要简单得多

总结几种常见的优化方案:

跟套接字部分提到的一样,增大套接字缓冲区大小以及UDP缓冲区范围

跟TCP部分提到的一样,增大本地端口号的范围

根据MTU大小,调整UDP数据包的大小,减少或者避免分片的发生

3.4 网络层

接下来,再来看网络层的优化

网络层,负责网络包的封装、寻址和路由,包括 IP、ICMP等常见协议

在网络层,最主要的优化其实就是对路由、 IP分片以及ICMP等进行调优

3.4.1 从路由和转发的角度出发

第一种,从路由和转发的角度出发,可以调整下面的内核选项

在需要转发的服务器中,比如用作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 问题

3.4.2 从分片的角度出发

第二种,从分片的角度出发,最主要的是调整MTU(Maximum Transmission Unit)的大小

通常,MTU的大小应该根据以太网的标准来设置

以太网标准规定,一个网络帧最大为1518B,那么去掉以太网头部的18B后,剩余的1500就是以太网MTU的大小

在使用VXLAN、GRE等叠加网络技术时,要注意,网络叠加会使原来的网络包变大导致MTU也需要调整

比如,就以VXLAN为例,它在原来报文的基础上增加了14B的以太网头部、 8B的VXLAN头部、8B的UDP头部以及20B的IP头部,换句话说,每个包比原来增大了50B

所以,需要把交换机、路由器等的MTU增大到1550,或者把VXLAN封包前(比如虚拟化环境中的虚拟网卡)的MTU减小为1450

另外,现在很多网络设备都支持巨帧,如果是这种环境还可以把MTU调大为9000,以提高网络吞吐量

3.4.3 从ICMP的角度出发

第三种,从ICMP的角度出发,为了避免ICMP主机探测、ICMP Flood等各种网络问题, 可以通过内核选项来限制ICMP的行为,比如:

可以禁止 ICMP 协议,即设置net.ipv4.icmp_echo_ignore_all = 1,这样,外部主机就无法通过 ICMP来探测主机

可以禁止广播 ICMP,即设置net.ipv4.icmp_echo_ignore_broadcasts = 1

3.5 链路层

网络层的下面是链路层,所以最后再来看链路层的优化方法

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

接下来,从不同的几个方面分别来看看

3.5.1 中断处理调度到不同CPU

由于网卡收包后调用的中断处理程序(特别是软中断)需要消耗大量的CPU,所以将这些中断处理程序调度到不同的CPU上执行就可以显著提高网络吞吐量。这通常可以采用下面两种方法:

可以为网卡硬中断配置CPU亲和性(smp_affinity),或者开启irqbalance服务

可以开启RPS(Receive Packet Steering)和RFS(Receive Flow Steering),将应用程序和软中断的处理调度到相同CPU上,这样可以增加CPU缓存命中率,减少网络延迟

3.5.2 卸载到网卡通过硬件执行

现在的网卡都有很丰富的功能,原来在内核中通过软件处理的功能,可以卸载到网卡中通过硬件来执行

TSO(TCP Segmentation Offload)和UFO(UDP Fragmentation Offload)

在TCP/UDP 协议中直接发送大包,而TCP包的分段(按照MSS分段)和UDP的分片(按照MTU分片)功能由网卡来完成

GSO(Generic Segmentation Offload)

在网卡不支持TSO/UFO时,将TCP/UDP包的分段,延迟到进入网卡前再执行

这样,不仅可以减少CPU的消耗,还可以在发生丢包时只重传分段后的包

LRO(Large Receive Offload)

在接收TCP分段包时,由网卡将其组装合并后,再交给上层网络处理

不过要注意,在需要IP转发的情况下不能开启LRO,因为如果多个包的头部信息不一致,LRO合并会导致网络包的校验错误

GRO(Generic Receive Offload)

GRO修复了LRO的缺陷,并且更为通用,且同时支持TCP和UDP

RSS(Receive Side Scaling)

也称为多队列接收,它基于硬件的多个接收队列来分配网络接收进程,这样可以让多个CPU来处理接收到的网络包

VXLAN卸载

让网卡来完成VXLAN的组包功能

3.5.3 网络接口优化

最后,对于网络接口本身,也有很多方法可以优化网络的吞吐量

开启网络接口的多队列功能

这样,每个队列就可以用不同的中断号调度到不同CPU上执行,从而提升网络的吞吐量

增大网络接口的缓冲区大小及队列长度

提升网络传输的吞吐量(注意,这可能导致延迟增大)

配置QoS

使用Traffic Control工具,为不同网络流量配置QoS

到这里,从应用程序、套接字、传输层、网络层,再到链路层,分别介绍了相应的网络性能优化方法

通过这些方法的优化后,网络性能可以满足绝大部分场景

四、小结

梳理常见的Linux网络性能优化方法

在优化网络的性能时,可以结合Linux系统的网络协议栈和网络收发流程,从应用程序、套接字、传输层、网络层再到链路层等对每个层次进行逐层优化

实际上,分析和定位网络瓶颈也是基于这些网络层进行的,而定位出网络性能瓶颈后就可以根据瓶颈所在的协议层进行优化。具体而言:

在应用程序中,主要是优化I/O模型、工作模型以及应用层的网络协议

在套接字层中,主要是优化套接字的缓冲区大小

在传输层中,主要是优化TCP和UDP协议

在网络层中,主要是优化路由、转发、分片以及ICMP协议

在链路层中,主要是优化网络包的收发、网络功能卸载以及网卡选项

 

   
1289 次浏览       17
相关文章

一文了解汽车嵌入式AUTOSAR架构
嵌入式Linux系统移植的四大步骤
嵌入式中设计模式的艺术
嵌入式软件架构设计 模块化 & 分层设计
相关文档

企点嵌入式PHP的探索实践
ARM与STM简介
ARM架构详解
华为鸿蒙深度研究
相关课程

嵌入式C高质量编程
嵌入式操作系统组件及BSP裁剪与测试
基于VxWorks的嵌入式开发、调试与测试
嵌入式单元测试最佳实践

最新活动计划
UAF架构体系与实践 5-17[北京]
用户体验与界面设计 5-22[北京]
MBSE(基于模型的系统工程)6-20[北京]
大模型微调原理与实操 6-20[厦门]
图数据库与知识图谱 6-27[北京]
Linux内核编程及设备驱动 7-25[北京]
 
 
最新文章
基于FPGA的异构计算在多媒体中的应用
深入Linux内核架构——简介与概述
Linux内核系统架构介绍
浅析嵌入式C优化技巧
进程间通信(IPC)介绍
最新课程
嵌入式Linux驱动开发
代码整洁之道-态度、技艺与习惯
嵌入式软件测试
嵌入式C高质量编程
嵌入式软件可靠性设计
成功案例
某军工所 嵌入式软件架构
中航工业某研究所 嵌入式软件开发指南
某轨道交通 嵌入式软件高级设计实践
深圳 嵌入式软件架构设计—高级实践
某企业 基于IPD的嵌入式软件开发
更多...