■ 河南 刘京义
编者按:笔者遇到一例服务器异常丢包的问题,一般来说该故障大多是由带宽被占满引发的,但这次的故障却并不是带宽引起的,究竟是什么原因呢?
某单位数据中心的一台服务器最近经常出现异常丢包的问题。其表现为在访问量不大的情况下一切正常,但是如果数据流量很大时,就会不断出现丢包的问题,给正常的业务带来的很大的影响。
该服务器安装的是Cent OS 6.X系统,使用的是四核的CPU和16GB的内存。既然是丢包故障,很可能是因为带宽占满引发的,但是查看相关的监控信息,发现其使用的千兆网卡上流量才几百兆每秒,并没有占满带宽,显然并不是因为该问题引发的。
登录到该机上,执行“top”命令,在返回信息中的“load average”栏中显示系统的负荷并不大,在“Kib Mem”栏中显示内存使用量一般,还有大量的空闲内存,显然并不是因为系统超负荷运行导致的上述问题。
但 是,在“%Cpu(s):”栏中的“si”项的数值为39.1,对 于“si”(即time spent servicing software interrupts)来说,表示的是软中断占用CPU资源的百分比,这里的数值明显过高,上述故障是不是因为中断异常造成的呢?
因此,执行“cat/proc/interrupts”命令,查看系统中断情况。在返回信息中显示所有设备的中断分布参数,其中第一列为具体的中断 号,从“cpu0”到“cpu3”各列中显示各CPU核心处理的中断数量。在最后一列显示与中断号对应的硬件设备名称,不同的设备使用的中断号是不同的。例如,时钟的中断号为0,键盘中断号为1,硬盘中断号为15,网卡中断号为16等。并且中断是具有优先级的,中断号越小其拥有的优先级越高。
从网卡“eth0”对应的中断分布情况来看,从系统启动至今CPU1处理的中断数量最多,达到几百万次。但是其他几个CPU核心处理的中断则很少,甚至为0,这说明多核CPU对于网卡中断的处理是不均衡的。
对于不同的计算机硬件来说,其和CPU进行通讯实际上是通过中断实现的。例如当网卡接收到数据包时,就会产生中断信号给CPU,CPU就会中断当前的工作,通知系统内核有新的数据包到来,内核就会调用中断处理程序加以响应,将数据包从网卡缓存区中复制到内存进行处理。
如果没有中断机制,那么当网卡缓冲区发生溢出时,就会出现数据包被丢弃的问题。CPU利用中断号来区分不同的硬件,计算机中的不同硬件拥有不同的中断号。中断其实就是一种电信号,由硬件产生并发送到中断控制器上。中断分为硬中断和软中断,前者是由硬件主动产生,处理速度很快,可以通过CPU屏蔽位进行屏蔽。后者是由软件发送给系统内核的中断信号,响应速度较慢,属于指令方式不能屏蔽。
执行“mpstat -P ALL 2”命令,按照每隔两秒的频率查看所有CPU核心的状态信息,在其中的“CPU”列中显示所有的CPU核心数,在“%soft”列中显示对应CPU核心处理的软中断数量,在“%irq”列中显示其处理的硬中断数量。可以看到只有CPU1在忙于处理软中断,其余的CPU核心则处于则比较空闲。因为网卡的中断号为16,因此执行“cat/proc/irq/16/smp_affinity”命令,查看中断亲缘性配置信息。
其对应的参数为“smp_affinity”,显示的数值为2,其使用的是十六进制,对应的二进制为“0010”,对应的就是CPU1。如果显示为1/4/8/10/20/40/80的话,对应的是CPU0/2/4/5/6/7/8等。与“smp_affinity”参数关联还有“smp_affinity_list”,其采用的是十进制。两者的配置信息起着相同的作用。
例如,执行“echo 1/proc/irq/16/smp_affinity_list”命令,可以激活CPU0核心处理软中断。再次执行“mpstat -P ALL 2”命令,可以看到CPU0已经开始处理大量的中断信息了。
根据以上分析,对于多核CPU来说,在处理网卡中断时其实并没有发挥真正的作用,仅仅是其中某个CPU核心在应对大量的中断请求。
这对于一般的应用场景来说问题不大,但是对于高流量的服务器来说,就显示难堪重负了。因为高性能的服务器需要将不同的网卡队列绑定到不同的CPU核心上,这样可以将网卡数据包产生的中断负载均衡的分布到不同的CPU核心上,避免出现某个CPU核心忙碌,其它核心闲置的情况,来尽可能的提高多核CPU处理中断请求的能力,提高服务器整体的数据吞吐能力。
上述故障就是因为只有某个CPU核心处理网卡中断,无法有效应对大量的网卡数据包所产生的中断请求,数据包无法全部复制到内存加以处理,造成网卡缓冲区溢出引发丢包故障。
解决的方法是,让所有的CPU核心都参与处理网卡中断请求,执行“echo f/proc/irq/16/smp_affinity”命令,让4个CPU核心全部都用来处理网卡中断,因为“f”的二进制值为“1111”,表示启用4个CPU核心。
注意,对于单队列网卡来说,仅仅执行上述配置其实是没有效果的。
在CentOS6.X及其以上系统中,内置了RPS(Receive Packet Steering)和RFS(Receive Flow Steering)功能,可以在软件层面模拟硬件的多队列网卡功能。RPS可以对网卡接收包中断中断进行优化,将一个或者多个队列中的软中断软中断分布到多个CPU核心上。RFS可以应对应用程序所在的CPU核心和中断处理的CPU核心不一致的情况。
将两者分配到同一个CPU核心上。执行“echo f/sys/class/net/eth0/queues/rx-0/rps_cpus”命令,来配置RPS功能。
执行“sysctl net.core.rps_sock_flow_entries=32768”和“echo 32768/sys/class/net/eth0/queues/rx-0/rps_flow_cnt”命令。配置系统相关核心参数,这里采用的是默认值。当然,网卡的名称需要根据实际情况更改。再次执行“mpstat -P ALL 2”命令,可以看到所有的CPU核心都开始处理网卡中断请求了。当然,也可以使用Irqbalance服务,来将中断平均分布到虽有的CPU核心上,可以有效提升系统性能。
执行“rpm -qa | grep irqbalance”命令,来 查看该服务安装情况。如果没有安装,可以执行“yum search irqbalance”和“yum install irqbalance”命令,来安装该服务。执行“service irqbalance start”命令,启动该服务。执行“ps -ef|grep irqbalance”命令,查看其运行情况。该服务可以自动将中断分布到所有的CPU核心上,来有效均衡中断的处理操作。
如果使用的是多队列网卡的话,那么就拥有了硬件 的RSS(Receive Side Scaling)功能。它可以根据网卡的硬件队列数量信息,将各队列的中断分布到多个CPU核心上。
注意,多队列网卡需要对应的驱动支持。执行“lspci-vvv”命令,如果返回信息中的“Ethernet controller”栏中存在“MSI-X,Enable+”之类的信息,在其后的“Count=”中数量大于1的话,就说明网卡支持多队列功能。
之后执行“grep eth0/proc/interrupts |awk'{print $NF}'”命 令,可显示网卡网卡支持的队列信息,例如从“eth0-TxRx-0”,“eth0-TxRx-1”到“eth0-TxRx-7”等。执行“grep eth0-TxRx/proc/interrupts |awk '{print$1,$NF}'”命 令,显示 各队列对应的中断号,例如从161到169等。除了使用irqbalance服务将对应的队列绑定到目标CPU核心外,还可以执行以下命令,将不同的队列绑定到CPU0到CPU4等核心上:
当然,如果是8核甚至更多的CPU的话,只需增加对应的的数量即可。
例如,可以执行“echo 80/proc/irq/169/smp_affinity”命令,将第8个队列绑定到CPU8核心上。如果已经启用了irqbalance服务,那么手动分配是无效的。
如果网卡队列和CPU核心数量不匹配,例如CPU是16核的,网卡队列数量为8,将队列分别绑定CPU0到7核心,虽然实现了对网卡中断的均衡处理,但是其余的CPU核心依然处于闲置状态。为此可以采用软件方式加以处理。
执行:
并以此类推直到执行“echo ffff/sys/class/net/eth0/queues/rx-7/rps_cpus”命令,将8个网卡队列全部绑定到所有的CPU核心上,其中的“ffff”表示16核CPU,命令执行8次,对应的接收队列名称从“rx-0”到“rx-7”。
执 行“sysctl net.core.rps_sock_flow_entries=32768”命令,配置RFS参数,来增加CPU缓存命中率,减少网络延迟。执行“ls/sys/class/net/eth0/queues/rx-*|grep queues|wc -l”命令,显示接收队列的数量。系统系统默认的“rps_sock_flow_entries”参数表示系统期望的同一时间的活跃连接数,默认值为“32768”。一般来说,它需要除以网卡接收队列数,来得到“rps_flow_cnt”参数的值,该参数和RFS密切相关。这里假设为8个接收队列,所以其值为4096。
执行以下命令:
并以此类推,直到执行“echo 4096/sys/class/net/eth0/queues/rx-7/rps_flow_cnt”命令,分别设置所有接收队列中“rps_flow_cnt”参数的值。这样,就可以将网卡所有接收队列绑定到16个CPU核心上,让所有的CPU核心都可以处理网卡中断。
当然,这里仅仅是举例说明,具体的网卡名称和队列名称需要根据实际情况来定。