Chinaunix首页 | 论坛 | 博客
  • 博客访问: 338903
  • 博文数量: 32
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 575
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-31 11:38
个人简介

实践主义者,多行路远胜于多读书。

文章分类

分类: 服务器与存储

2015-10-23 14:25:54

最近在做高负载的udp网络I/O测试工作,测试使用的主机包括:
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,这个在网上搜索出很多方法:
  1. ls /sys/class/net/eth0/queues/ 查看里面是不是有多个 rx-* , 有几个就代表有同个队列。
  2. lspci -vvv | grep MSI   跟 MSI 相关的条目后面都会有个 "Enable" 的 flag,后面的 "+" 代表开启,"-" 代表禁用。
    MSI方式的中断对多核cpu的利用情况不佳,网卡中断全部落在某一个cpu上,
    即使设置cpu affinity也没有作用,而MSI-X中断方式可以自动在多个cpu上分担中断。
  3. cat /proc/interrupts | grep eth0  看计数是不是集中在一个CPU核中
  4. grep -A 10 -i network /var/log/dmesg
  5. 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一起分担软中断才对,可实际结果只有两个,有知道原因的请赐教,但是不管怎么说,两个总好过一个。




阅读(8304) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~