加强UDP交互性能
这部分重点参考个人认为非常非常厉害的文案。讲述了怎样提高UDP流的处理速率,但实质触及的技术点不仅限于UDP。这儿结合这篇文案的观点和自己在实质工作中的有些经验做一下总结和记录。
提高网络性能的基本技术
TSO/GSO/GRO
这些技术用于报文分割/聚合以减少报文在服务中的处理,适用于TCP 字节流(运用UDP隧道的TCP亦能够)
不适用于UDP数据报(除了UFO,其依赖理学NICs),GSO针对UDP来讲便是IP分片,因此收益不大,不外此刻MLX又推出了一种USO的技术,支持了UDP分段,类比TCP的TSO,每一个分片中包括了完整的UDP header。
TSO/GSO用于发送报文时,将上层聚合的数据进行分割,分割为不大于MTU的报文;GRO在接受侧,将多个报文聚合为一个数据,上送给协议栈。总之便是将报文的处理下移到了网卡上,减少了网络栈的包袱。TSO/GSO等能够增多网络吞吐量,但有可能导致某些连接上的网络延迟。
RSS
· 在多核服务器上扩展了网络接收侧的处理
· RSS本身是一个NIC特性
· 将报文分发到一个NIC中的多个队列上
· 每一个队列都有一个区别的中断向量(区别队列的报文能够被区别的核处理)
· 能够运用于TCP/UDP
· 一般10G的NICs会支持RSS
RSS是理学网卡支持的特性,能够将NIC的多个队列映射到多个CPU核上进行处理,增多处理的效率,减少CPU中断竞争。
RSS有一个间接表,用于确定分发的报文所属的队列,能够运用ethtool -x命令查看(虚拟环境可能不支持)
中断绑定
RSS只能将区别的流量分散到区别的队列(每一个队列对应一个收发中断),充分利用网卡多队列的特性,但其不可保准中断的平衡,如下图所示。
softirq仅在NUMA的Node0上运行,想要充分利用其它CPU,就要触及到中断绑定。
中断绑定关联配置 smp_affinity
十六进制的bitmask,指定中断亲和性 $ cat /proc/irq/$irq/smp_affinity 00000000,00000000,00000000,00000002 smp_affinity_list
十进制CPU list,指定中断亲和性 $ cat /proc/irq/$irq/smp_affinity_list 1 /etc/init.d/irqbalance
重点是在系统性能与功耗之间平衡的程序,系统负荷重时把irq分配到多个CPU上,系统空闲时把irq分配在少量CPU保准其它CPU的睡觉状态。用户可收到设置中断的affinity,禁用irqbalance的决策 affinity_hint
告诉irqbalance此中断倾向的CPU亲和性。 exact: irqbalance程序会严格根据内核的affinity_hint值进行亲和性平衡; subset: 暗示irqbalance会以affinity_hint的一个子集进行平衡; ignore: 暗示完全忽略内核的affinity_hint。 $ cat /proc/irq/$irq/affinity_hint 00000000,00000000,00000000,00000000
因此又两种办法处理中断平衡的问题,一个是把irqbalance服务stop,手动设置中断的smp_affinity,另一种设设置好affinity_hint,而后让irqbalance运用-h exact参数起步。
RPS
中断绑定后的效果如下图所示,因为一共仅有16个队列,因此能够看到其绑定的16个队列的软中断已然将对应CPU所有打满了。
可是咱们的及其还有其他空闲的4个CPU,怎样充分利用起来这4个CPU呢。这就需要RPS了。PS:实质应用中意见将RPS所有关闭,由于打开可能引起性能更加恶化,详见下文分析,除非对性能调优有较多经验。RPS能够指定CPU和软中断的关系(中断绑定指的是定CPU和中断的关系)
# echo 10040 > /sys/class/net/ens1f0/queues/rx-6/rps_cpus
# echo 20080 > /sys/class/net/ens1f0/queues/rx-7/rps_cpus
# echo 40100 > /sys/class/net/ens1f0/queues/rx-8/rps_cpus
# echo 80200> /sys/class/net/ens1f0/queues/rx-9/rps_cpus
设置RPS后,在查看各个CPU的状况:
sar -u ALL -P ALL 1
虽然软中断相对更加均匀了,然则实质的pps性能却下降了非常多。咱们运用perf进行性能瓶颈分析:
perf record -a -g -- sleep 5
能够看到瓶颈重点来自以下函数调用:
queued_spin_lock_slowpath:锁竞争
udp_queue_rcv_skb:需求socket锁
这儿就需要触及到socket锁的问题。
C/C++Linux服务器研发/后台架构师【零声教育】-学习视频教程-腾讯课堂
【文案福利】:博主整理了有些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的能够自动添加哦!~点击加入(832218493需要自取)
Socket锁
咱们的服务器在一个特定端口上仅绑定了一个socket;
每一个内核的softirq同期将报文推入socket队列,最后引起socket锁竞争。
为了避免锁竞争,能够运用内核的SO_REUSEPORT套接字选项分割sockets,效果如下:
l 该选项在内核3.9引入,默认运用流(报文首部)哈希来选取socket
l SO_REUSEPORT准许多个UDP socket绑定到相同的端口上,在每一个报文排队时选取一个套接字 int on = 1;intsock = socket(AF_INET, SOCK_DGRAM,0);
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
bind(sock, ...);SO_REUSEPORT的介绍能够参考这篇文案。
运用SO_REUSEPORT后pps性能得到了较大的提高,继续分析热点瓶颈,如下:
能够看到,仍然有socket锁竞争。SO_REUSEPORT默认运用流哈希来选取队列,区别的CPU核可能会选取相同的sockets,引起竞争。
避免socket锁竞争
为了避免按照哈希选socket队列引起的锁冲突,咱们能够按照CPU核号选取socket
l 经过 SO_ATTACH_REUSEPORT_CBPF/EBPF实现
l 在内核4.5引入以上功能
此时软中断之间再也不产生竞争
SO_ATTACH_REUSEPORT_EPBF用法能够参见内核源码树中的例子:tools /testing/selftests /net /reuseport_bpf_cpu.c。
线程亲和性
虽然咱们的用户线程数:sockets数 == 1:1,但不必定与软中断处在同一CPU核。
为了将用户现场固定到相同的核,得到更好的缓存亲和性。能够运用cgroup, taskset, pthread_setaffinity_np()等方式制定线程的cpu亲和性。
制定亲和性后,咱们的pps性能又得到了进一步提高。
输出方向的锁
到日前为止处理的问题都处在接收方向上,发送方向是不是有锁竞争?
· 内核拥有Qdisc(默认的Qdisc为pfifo_fast)
· 每一个Qdisc都连接到NIC的tx队列
· 每一个Qdisc都有自己的锁
· Qdisc默认经过流哈希进行选取,因此呢可能会发送锁竞争
为认识决发送方向的锁竞争就需要借助于XPS功能,XPS准许内核选取按照CPU核号选取Tx队列(Qdisc)。
PS:在虚拟机环境中通常运用virtio-net驱动,而virtio-net默认运用per-cpu变量来选取队列,因此呢XPS并不生效,然则在3.13 kernel版本之上对这个问题进行了修该,使得virtio-net亦能运用XPS。
XPS的运用方式和RPS类似: # echo 00001 > /sys/class/net/<NIC>/queues/tx-0/xps_cpus
# echo 00002 > /sys/class/net/<NIC>/queues/tx-1/xps_cpus
# echo 00004 > /sys/class/net/<NIC>/queues/tx-2/xps_cpus
# echo 00008 > /sys/class/net/<NIC>/queues/tx-3/xps_cpus
...优化单个核
为了进一步加强性能,需要降低单个核的开销。
1. 禁用GRO
能够看到默认启用了GRO,并消耗了4.9%的CPU时间。GRO并不适用于UDP(UDP隧道除外,如VXLAN)
为UDP服务禁用GRO
# ethtool -K gro off
重视:倘若关注TCP性能,则不可禁用GRO功能,禁用GRO会引起TCP接收吞吐量降低。
2. 卸载iptables
因为iptables是内核加载的模块,即运用户不需要任何规则,它内部亦会消耗一部分CPU
即使不添加任何规则,某些发行版亦会加载iptables模块
倘若不需要iptables,则卸载该模块
# modprobe -r iptable_filter
# modprobe -r ip_tables
3. 关闭反向路径过滤和本地位置校验
在接收路径上,因为反向路径过滤和本地位置校验,FIB查找了两次。每次亦有额外的CPU开销。
倘若不需要源校验,则能够忽略
# sysctl -w net.ipv4.conf.all.rp_filter=0
# sysctl -w net.ipv4.conf..rp_filter=0
# sysctl -w net.ipv4.conf.all.accept_local=1
4. 禁用audit
当海量处理报文时,Audit消耗的CPU会变大。大概消耗2.5%的CPU时间
倘若不需要audit,则禁用
# systemctl disable auditd
# reboot
TCP性能优化
相对UDP来讲,TCP的性能优化要繁杂的多。TCP的性能重点受到以下几个方面的影响:
l 内核版本
l 拥塞算法
l 延时(rtt)
l 接收buffer
l 发送buffer
首要,内核版本对网络性能有巨大影响,由于高版本的内核不仅对协议栈做了海量优化,况且对协议栈用到的其他机制,如内存分配等有着非常多优化,倘若测试能够发掘运用4.9或以上版本的内核,其TCP性能要远好于3.10版本。
其次,拥塞算法亦非常关键,例如在公网或延时(rtt)很强的状况运用BBR算法效果较好,而在内网延时较小的场景,cubic的效果可能更佳。
当前最平常的TCP的性能影响原因是丢包引起的重传和乱序,这种状况就需要分析详细链路的丢包原由了,如运用dropwatch工具查看内核丢包: https://blog.huoding.com/2016/12/15/574
咱们这儿重点讨论的是内核版本和拥塞算法一致,且无反常丢包场景问题。
TCP 性能和发送接收 Buffer 的关系
先看一下系统中和接收发送buffer关联参数。 $sudo sysctl -a | egrep "rmem|wmem|adv_win|moderate"
net.core.rmem_default = 212992
net.core.rmem_max = 212992
net.core.wmem_default = 212992
net.core.wmem_max = 212992
net.ipv4.tcp_adv_win_scale = 1
net.ipv4.tcp_moderate_rcvbuf = 1
net.ipv4.tcp_rmem = 4096 87380 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096
vm.lowmem_reserve_ratio = 256 256 32
TCP性能和发送窗口的关系
首要咱们看下面一个案例,此案例中TCP性能上不去,抓包分析发掘其序列号变化如下图,大概传输16k上下数据就会显现一次20ms的等待。
引起这个问题的原由是发送buffer仅有 16K ,这些包火速都发出去了,然则这 16K 不可立即释放出来填新的内容进去,由于 tcp 要保准靠谱,万一中间丢包了呢。仅有等到这 16K 中的某些包 ack 了,才会填充有些新包进来而后继续发出去。因为这儿 rt 基本是 20ms,亦便是 16K 发送完毕后,等了 20ms 才收到有些 ack,这 20ms 应用、内核什么都不可做。
sendbuffer 相当于发送仓库的体积,仓库的货物都发走后,不可立即腾出来发新的货物,而是要等对方确认收到了(ack)才可腾出来发新的货物。 传输速度取决于发送仓库(sendbuffer)、接收仓库(recvbuffer)、路宽(带宽)的体积,倘若发送仓库(sendbuffer)足够大了之后接下来的瓶颈便是高速公路了(带宽、拥塞窗口)。
因此能够经过调大发送buffer来处理此问题,发送buffer的关联参数有: net.core.wmem_max = 1048576
net.core.wmem_default = 124928
net.ipv4.tcp_wmem = 4096 16384 4194304
net.ipv4.udp_wmem_min = 4096
其关系如下:
?倘若指定了 tcp_wmem,则 net.core.wmem_default 被 tcp_wmem 的覆盖
?send Buffer 在 tcp_wmem 的最小值和最大值之间自动调节
?倘若调用 setsockopt()设置了 socket 选项 SO_SNDBUF,将关闭发送端缓冲的自动调节机制,tcp_wmem 将被忽略
?SO_SNDBUF 的最大值由 net.core.wmem_max 限制
?默认状况下 Linux 系统会自动调节这个 buffer(net.ipv4.tcp_wmem), 亦便是不举荐程序中主动去设置 SO_SNDBUF,除非知道晓得设置的值是最优的
TCP性能和接收窗口的关系
接收buffer很小的时候并且rtt很小时对性能的影响
咱们能够看下面一个例子:显著看到接收窗口经常跑满,然则由于 rtt 很小,一旦窗口空出来火速就通告到对方了,因此全部过小的接收窗口亦没怎么影响到整体性能。
下图能够更清楚看到 server 一旦空出来点窗口,client 马上就发送数据,因为这点窗口太小,rtt 是 40ms,亦便是一个 rtt 才可传 3456 字节的数据,全部带宽才 80-90K,完全没跑满。
接收buffer很小的时候并且rtt很小时对性能的影响
倘若一样的测试在 rtt 是 0.1ms 的话:虽然显著看到接收窗口经常跑满,然则由于 rtt 很小,一旦窗口空出来火速就通告到对方了,因此全部过小的接收窗口亦没怎么影响到整体性能。
从这儿能够得出结论,接收窗口的体积对性能的影响,rtt越大影响越显著,当然这儿还需要应用程序协同,倘若应用程序始终不读走数据即使接收窗口再大亦会堆满的。
小结通常来讲绝对不要在程序中手工设置 SO_SNDBUF 和 SO_RCVBUF,内核自动调节做的更好; SO_SNDBUF 通常会比发送滑动窗口要大,由于发送出去并且 ack 了的才可从 SO_SNDBUF 中释放; TCP 接收窗口跟 SO_RCVBUF 关系很繁杂;SO_RCVBUF 太小并且 rtt 很大的时候会严重影响性能; 接收窗口比发送窗口要繁杂的多; 再拥塞算法必定的状况下,发送buffer、rtt、接收buffer一块决定了传输速度。
|