1. 真实主机,
2. VMWare9 虚拟机,
3. 某云虚拟机。
操作系统均是Ubuntu-14 64bits,kernel版本是 3.1x.x.
不论哪种环境,以下结论是一致的:
1. 当udp包的负载较小时,例如每个udp报文都只有50字节,那么所谓的接入带宽是跑不满的,
具体能跑多少,取决于整体环境(包括交换机/路由器,网卡驱动,内核参数)的影响。
简单来说,就是包裹数太多,看哪个环节是短板。
用iperf可以测试:
server端:iperf -u -w 1024000 -s
client端:iperf -c server_ip -u -b 10M -l 50 (按10mbps带宽测试,每个udp包长度是50字节)
2. 单次sendto的资源占用要高于recvfrom,
前提是sendto中指定对端地址,而不是采用先connect的方式。
3. I/O密集时,用select来检测可读事件,其占用的CPU是很大的,
因为系统调用次数太多,可以用以下命令来查看每个系统资源的消耗:
strace -c -T -p xxxx (xxxx 是具体干活的线程id)
4. I/O密集时,多线程会比单线程略好,前提是线程数不超过主机CPU核数,
但这个基本改善不大,如果系统还有其它负荷,会带来CPU切换的消耗。
5. 调优一些内核参数可能有改观,如果CPU能力是瓶颈的话:
net.core.netdev_budget = 5000 (仅适用于NAPI驱动的网卡,用ethtool -i eth0 查看)
8169网卡的主机输出:
driver: r8169
version: 2.3LK-NAPI
firmware-version: rtl8168g-2_0.0.1 02/06/13
bus-info: 0000:02:00.0
supports-statistics: yes
supports-test: no
supports-eeprom-access: no
supports-register-dump: yes
supports-priv-flags: no
VMWare9 虚拟机的主机输出:
driver: e1000
version: 7.3.21-k8-NAPI
firmware-version:
bus-info: 0000:02:01.0
supports-statistics: yes
supports-test: yes
supports-eeprom-access: yes
supports-register-dump: yes
supports-priv-flags: no
某云主机输出(啥有没有,不知道是不是NAPI驱动):
driver: vif
version:
firmware-version:
bus-info: vif-0
supports-statistics: yes
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no
net.core.netdev_max_backlog=500000
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_fin_timeout=6
#keep-alive
net.ipv4.tcp_keepalive_intvl = 5
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_time = 120
net.core.rmem_default = 204800 # 字节,指默认的socket 接收缓冲区
net.core.rmem_max = 4212992 # 不能超过此大小,即使是有setsockopt设置。
net.core.wmem_default = 204800 # 发送缓冲区
net.core.wmem_max = 4212992
net.ipv4.udp_mem = 400000 500000 800000 # PAGE_SIZE . (min, pressure , max) This is a vector of three integers governing the number of pages allowed for queueing by all UDP sockets.
net.ipv4.udp_rmem_min = 4096 # 字节 . Each UDP socket is able to use the size for receiving data, even if total pages of UDP sockets exceed udp_mem pressure.
net.ipv4.udp_wmem_min = 4096
tcp_mem(3个INTEGER变量):low, pressure, high
- low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。
- pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。
- high:允许所有tcp sockets用于排队缓冲数据报的页面量,当内存占用超过此值,系统拒绝分配socket,后台日志输出“TCP: too many of orphaned sockets”。
一般情况下这些值是在系统启动时根据系统内存数量计算得到的。 根据当前tcp_mem最大内存页面数是1864896,当内存为(1864896*4)/1024K=7284.75M时,系统将无法为新的socket连接分配内存,即TCP连接将被拒绝。
实际测试环境中,据观察大概在99万个连接左右的时候(零头不算),进程被杀死,触发out of socket memory错误(dmesg命令查看获得)。每一个连接大致占用7.5K内存(下面给出计算方式),大致可算的此时内存占用情况(990000 * 7.5 / 1024K = 7251M)。
net.ipv4.tcp_rmem = 4096 81920 4194304 # 字节net.ipv4.tcp_wmem = 4096 81920 4194304
net.ipv4.tcp_mem = 932448 1243264 1864896 # PAGE_SIZE
net.core.somaxconn = 8192
6. 网卡的中断始终是消耗CPU的一个核,所以多核CPU在这不能充分发挥作用。
用top看就是 si 集中在一个CPU核中:
top - 19:51:43 up 10:10, 7 users, load average: 0.90, 0.89, 0.69
Tasks: 254 total, 3 running, 251 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 1.0 sy, 0.0 ni, 98.0 id, 0.0 wa, 0.0 hi, 1.0 si, 0.0 st
%Cpu1 : 1.0 us, 2.0 sy, 0.0 ni, 97.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.0 us, 3.2 sy, 0.0 ni, 89.2 id, 0.0 wa, 0.0 hi, 7.5 si, 0.0 st
%Cpu3 : 0.0 us, 3.1 sy, 0.0 ni, 96.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
下面就是针对第6点来做优化,网卡是否支持多队列RSS,这个在网上搜索出很多方法:
- ls /sys/class/net/eth0/queues/ 查看里面是不是有多个 rx-* , 有几个就代表有同个队列。
-
lspci -vvv | grep MSI 跟 MSI 相关的条目后面都会有个 "Enable" 的 flag,后面的 "+" 代表开启,"-" 代表禁用。
MSI方式的中断对多核cpu的利用情况不佳,网卡中断全部落在某一个cpu上,
即使设置cpu affinity也没有作用,而MSI-X中断方式可以自动在多个cpu上分担中断。 - cat /proc/interrupts | grep eth0 看计数是不是集中在一个CPU核中
- grep -A 10 -i network /var/log/dmesg
- cat /proc/softirqs 看看 RX TX是不是集中在一个CPU核中。
但没关系,如果只有单队列,那么RPS(Receive Packet Steering)和 RFS(Receive flow steering)也能用上。
对于单队列设备,单队列的rps_flow_cnt值被配置成与 rps_sock_flow_entries相同:
echo 1024 > /proc/sys/net/core/rps_sock_flow_entries
echo 1024 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
如果网卡支持RSS,那么可以参考以下内容来设置:
--------------------------------------------------------------------------------------
以下内容摘自:
有修改
前些天发现XEN虚拟机上的Nginx服务器存在一个问题:软中断过高,而且大部分都集中在同一个CPU,一旦系统繁忙,此CPU就会成为木桶的短板。
在问题服务器上运行「top」命令可以很明显看到「si」存在异样,大部分软中断都集中在 1 号CPU上,其它的CPU完全使不上劲儿:
shell> top Cpu0: 11.3%us, 4.7%sy, 0.0%ni, 82.5%id, ... 0.8%si, 0.8%st Cpu1: 21.3%us, 7.4%sy, 0.0%ni, 51.5%id, ... 17.8%si, 2.0%st Cpu2: 16.6%us, 4.5%sy, 0.0%ni, 77.7%id, ... 0.8%si, 0.4%st Cpu3: 15.9%us, 3.6%sy, 0.0%ni, 79.3%id, ... 0.8%si, 0.4%st Cpu4: 17.7%us, 4.9%sy, 0.0%ni, 75.3%id, ... 1.2%si, 0.8%st Cpu5: 23.6%us, 6.6%sy, 0.0%ni, 68.1%id, ... 0.9%si, 0.9%st Cpu6: 18.1%us, 4.9%sy, 0.0%ni, 75.7%id, ... 0.4%si, 0.8%st Cpu7: 21.1%us, 5.8%sy, 0.0%ni, 71.4%id, ... 1.2%si, 0.4%st
查询一下软中断相关数据,发现主要集中在 NET_RX 上,猜测是网卡问题:
shell> watch -d -n 1 'cat /proc/softirqs' CPU0 CPU1 CPU2 ... CPU7 HI: 0 0 0 ... 0 TIMER: 3692566284 3692960089 3692546970 ... 3693032995 NET_TX: 130800410 652649368 154773818 ... 308945843 NET_RX: 443627492 3802219918 792341500 ... 2546517156 BLOCK: 0 0 0 ... 0 BLOCK_IOPOLL: 0 0 0 ... 0 TASKLET: 0 0 0 ... 0 SCHED: 1518716295 335629521 1520873304 ... 1444792018 HRTIMER: 160 1351 131 ... 196 RCU: 4201292019 3982761151 4184401659 ... 4039269755
查询宿主机,发现网卡其运行在单队列模式下:
shell> grep -A 10 -i network /var/log/dmesg Initalizing network drop monitor service Intel(R) Gigabit Ethernet Network Driver - version 3.0.19 igb 0000:05:00.0: Intel(R) Gigabit Ethernet Network Connection igb 0000:05:00.0: eth0: (PCIe:2.5GT/s:Width x4) 00:1b:21:bf:b3:2c igb 0000:05:00.0: eth0: PBA No: G18758-002 igb 0000:05:00.0: Using MSI-X ... 1 rx queue(s), 1 tx queue(s) igb 0000:05:00.1: Intel(R) Gigabit Ethernet Network Connection igb 0000:05:00.1: eth1: (PCIe:2.5GT/s:Width x4) 00:1b:21:bf:b3:2d igb 0000:05:00.1: eth1: PBA No: G18758-002 igb 0000:05:00.1: Using MSI-X ... 1 rx queue(s), 1 tx queue(s)
接着确认一下网卡的中断号,因为是单队列,所以只有一个中断号 45:
shell> grep eth /proc/interrupts | awk '{print $1, $NF}' 45: eth0
知道了网卡的中断号,就可以查询其配置「smp_affinity」:
shell> cat /proc/irq/45/smp_affinity 02
这里的 02 实际上是十六进制,表示 1 号CPU,计算方法如下:
Binary Hex CPU 0 0001 1 CPU 1 0010 2 CPU 2 0100 4 + CPU 3 1000 8 ----------------------- both 1111 f
说明:如果 4 个CPU都参与中断处理,那么设为 f;同理 8 个CPU的就设置成 ff:
shell> echo ff > /proc/irq/45/smp_affinity
此外还有一个类似的配置「smp_affinity_list」:
shell> cat /proc/irq/45/smp_affinity_list 1
两个配置是相通的,修改了一个,另一个会跟着变。不过「smp_affinity_list」使用的是十进制,相比较「smp_affinity」的十六进制,可读性更好些。
了解了这些基本知识,我们可以尝试换一个CPU试试看会发生什么: echo 7 > /proc/irq/45/smp_affinity_list
再通过「top」命令观察,会发现处理软中断的CPU变成了 7 号CPU。
说明:如果希望多个CPU参与中断处理的话,可以使用类似下面的语法:echo 3,5 > /proc/irq/45/smp_affinity_list ; echo 0-7 > /proc/irq/45/smp_affinity_list
坏消息是对单队列网卡而言,「smp_affinity」和「smp_affinity_list」配置多CPU无效。
注:这2项在我的实验中是管用的,且必须设置。否则仍然会只在一个核中处理中断。测试环境:VMware 虚机Ubuntu和真机Ubuntu.
好消息是Linux支持,通俗点来说就是在软件层面模拟实现硬件的多队列网卡功能。
首先看看如何配置RPS,如果CPU个数是 8 个的话,可以设置成 ff:
shell> echo ff > /sys/class/net/eth0/queues/rx-0/rps_cpus
接着配置内核参数rps_sock_flow_entries(官方文档推荐设置: 32768):
shell> sysctl net.core.rps_sock_flow_entries=32768
最后配置rps_flow_cnt,单队列网卡的话设置成rps_sock_flow_entries即可:
echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
说明:如果是多队列网卡,那么就按照队列数量设置成 rps_sock_flow_entries / N 。N是队列个数。
做了如上的优化后,我们再运行「top」命令可以看到软中断已经分散到了两个CPU:
shell> top Cpu0: 24.8%us, 9.7%sy, 0.0%ni, 52.2%id, ... 11.5%si, 1.8%st Cpu1: 8.8%us, 5.1%sy, 0.0%ni, 76.5%id, ... 7.4%si, 2.2%st Cpu2: 17.6%us, 5.1%sy, 0.0%ni, 75.7%id, ... 0.7%si, 0.7%st Cpu3: 11.9%us, 7.0%sy, 0.0%ni, 80.4%id, ... 0.7%si, 0.0%st Cpu4: 15.4%us, 6.6%sy, 0.0%ni, 75.7%id, ... 1.5%si, 0.7%st Cpu5: 20.6%us, 6.9%sy, 0.0%ni, 70.2%id, ... 1.5%si, 0.8%st Cpu6: 12.9%us, 5.7%sy, 0.0%ni, 80.0%id, ... 0.7%si, 0.7%st Cpu7: 15.9%us, 5.1%sy, 0.0%ni, 77.5%id, ... 0.7%si, 0.7%st
疑问:理论上讲,我已经设置了RPS为ff,应该所有 8 个CPU一起分担软中断才对,可实际结果只有两个,有知道原因的请赐教,但是不管怎么说,两个总好过一个。