大量小包的CPU密集型系统调优案例一则

我的blog前面有一篇文章描述了软终端导致单cpu消耗100%,导致机器丢包跟延迟高的问题,文中我只是简单的说明了一下升级内核进行解决的,这个问题我并没有进行一个问题解决的说明,经历了一系列的调整后,单机的并发从单机单网卡承受100M流量到160M流量,到现在的最高的230M流量,在程序没有大规模修改的情况下效果还是十分的明显,这次这篇文章将完整的说一下我的一个解决方法:
先说说我的场景,我目前负责的一个项目,大量的小数据包,长连接,每个数据包都不大,大概10Kbit左右一个包,但是数量十分之大,目前在生产环境中最大的数据包数量高达15W/s的数量,常见的网游系统,小图片cdn系统,这些服务类型都算是这种类型,单网卡流量不大,但是数据包数量极大,我目前调优的结果是
在Xeon E5504, BCM5716的网卡,8G的dell r410的机器,单网卡实现了230MBits大的流量,系统的load为0,8颗cpu每一颗还有10%左右的IDLE,由于我们的系统是数据包的转发,还有一个网卡同期的流量使220M,12.8W的数据包,算上总数,大概可以到450MBits的流量,25.5W的小包,由于人数有限,流量没有跑上去,预计可以跑到480MBits的流量,生产环境的一台机器的数据:
流量及机器的网卡包数量
traffic
机器的cpu消耗
top

首先机器的选型,由于大量小包的cpu密集的系统,当然cpu越性能越高越好咯,但是成本相应的高。对于这种类型的机器,网卡选型也是十分的重要,一定要选择支持msi-x的网卡类型,什么是msi-x大家可以查询google资料去了解一下,目前市面出售的大部分最新的网卡都有这个功能,查看方法lspci -v,看到如下图的内容
MSI-X
再者网卡是否支持多队列,多队列网卡十分的重要,不是多队列的网卡,这篇文章几乎不需要看了,可以直接忽略掉,查看方法cat /proc/interrupts,这个方法并不适用所有的操作系统例如在rhel 5.5的os当中,bcm5716的网卡就看不到,具体我也没有查到怎么查看的方法,麻烦知道的用户告知一声,如果是的话应该可以看到如下图的内容
multiQ
每一个网卡有8个队列,对于这种大量小包的cpu密集型的系统,多队列的网卡性能至少提供性能50%以上,我们生产环境的有台非多队列的Intel 82574的网卡调优后只能跑到160M左右流量,跟上图明显的一个对比.而同等情况下买一个多队列的网卡明显要便宜很多。
操作系统的选择,目前大部分企业使用的是rhel系列的os,包括标准的rhel跟centos作为一个生产环境的os,目前主力的版本还是rhel 5系列的os,而rhel 5系列的内核版本对于软中断处理并不是很好,调优的结果不是很理想,在rhel 5系列的os上,我们最高流量单网卡也就是160M左右,而且机器的load也很高了,机器已经出现小量的丢包,而且只是使用了4到6个cpu还有几个cpu没有利用上,机器性能没有挖掘完毕,由于我们的机器没有存储的压力,单纯的只是消耗cpu资源,没有io的压力,于是大胆的启用刚出的rhel 6.1的系统,看重这个系统的原因是,该os的内核已经加入了google的两个原本在2.6.35当中才启用的2个补丁——RPS/RFS,RPS主要是把软中断的负载均衡到各个cpu,由于RPS只是单纯把数据包均衡到不同的cpu,这个时候如果应用程序所在的cpu和软中断处理的cpu不是同一个,此时对于cpu cache的影响会很大,那么RFS确保应用程序处理的cpu跟软中断处理的cpu是同一个,这样就充分利用cpu的cache,默认情况下,这个功能并没有开启,需要手动开启开启方法,开启的前提是多队列网卡才有效果。

echo ff > /sys/class/net/<interface>/queues/rx-<number>/rps_cpus
echo 4096 > /sys/class/net/<interface>/queues/rx-<number>/rps_flow_cnt
echo 30976 > /proc/sys/net/core/rps_sock_flow_entries

对于2个物理cpu,8核的机器为ff,具体计算方法是第一颗cpu是00000001,第二个cpu是00000010,第3个cpu是00000100,依次类推,由于是所有的cpu都负担,所以所有的cpu数值相加,得到的数值为11111111,十六进制就刚好是ff。而对于/proc/sys/net/core/rps_sock_flow_entries的数值是根据你的网卡多少个通道,计算得出的数据,例如你是8通道的网卡,那么1个网卡,每个通道设置4096的数值,8*4096就是/proc/sys/net/core/rps_sock_flow_entries的数值,对于内存大的机器可以适当调大rps_flow_cnt,这个时候基本可以把软中断均衡到各个cpu上了,而对于cpu的使用,还有其它的例如use,sys等,这个不均衡的话,cpu还是会浪费掉,同时对我们的程序针对多cpu进行小部分的开发跟重新编译,本身我们程序就是多进程的一个模型,我们采用nginx的进程管理模型,一个master管理work进程,master分配每一个连接给work进程,由work进程处理用户的请求,这样每一个进程都能均衡负担几乎相同的处理请求,同时在6.1的系统中gcc新增一个openmp的指令,这个指令作用针对多核,增加程序的并行计算的功能,不需要大规模的更改代码就能实现多核的并行性计算,具体使用使用方法请见如下url

http://zh.wikipedia.org/zh/OpenMP

针对上面的处理,基本上可以实现cpu按理说可以实现完全的均衡了,但是当我们在实际的使用过程中发现还是cpu还不是100%的均衡,存在1到2个cpu消耗量还是比其它的要大20%左右,导致在高峰期有1到2个cpu的idle使用完毕,导致用户使用存在卡的情况,这个时候,需要手动调节一下cpu的使用情况,在这操作之前先了解几个名词以及其作用
一个是IO-APIC(输入输出装置的高级可编程中断控制器)
为了充分挖掘 SMP 体系结构的并行性,能够把中断传递给系统中的每个CPU至关重要,基于此理由,Intel 引入了一种名为 I/O-APIC的东西。该组件包含两大组成部分:一是“本地 APIC”,主要负责传递中断信号到指定的处理器;举例来说,一台具有三个处理器的机器,则它必须相对的要有三个本地 APIC。另外一个重要的部分是 I/O APIC,主要是收集来自 I/O 装置的 Interrupt 信号且在当那些装置需要中断时发送信号到本地 APIC。这样就能充分利用多cpu的并行性。如果用户对于IO-APIC更感兴趣,请见如下url的中的pdf的说明

http://wenku.baidu.com/view/ccdc114e2e3f5727a5e962e9.html

另外一个就是irqbalance
irqbalance 用于优化中断分配,它会自动收集系统数据以分析使用模式,并依据系统负载状况将工作状态置于 Performance mode 或 Power-save mode.处于 Performance mode时irqbalance 会将中断尽可能均匀地分发给各个CPU以充分利用 CPU 多核,提升性能.处于 Power-save mode时,irqbalance 会将中断集中分配给第一个 CPU,以保证其它空闲 CPU 的睡眠时间,降低能耗
通过这我们就发现我们是一个非常繁重的系统,并没有节能的需求,而是需要充分利用各个cpu的性能,而事实上在一个大量小包的系统上,irqbalance优化几乎没有效果,而且还使得cpu消耗不均衡,导致机器性能得不到充分的利用,这个时候需要把它给结束掉
/

etc/init.d/irqbalance stop

同时,手动绑定软中断到指定的cpu,对于一个8个队列的网卡,8核的机器,可以指定一个cpu处理一个网卡队列的中断请求,并根据cpu的消耗情况,手动调整单个网卡的队列到资源消耗低的cpu上,实现手动均衡,具体操作方法,执行如下命令

cat /proc/interrupts

intrqnum

计算cpu的方法第一颗为00000001换算成16进制为1,第2颗cpu为00000010换算成16进制为2,依次类推得出,第8颗cpu为80,这样就可以做如下的绑定了

echo 0001 > /proc/irq/<number>/smp_affinity

这样就可以绑定中断到指定的cpu了,这个时候有可能会问,我的机器是一个2通道的网卡,这个时候如果一个通道一个cpu绑定,这个时候就浪费了6颗cpu了,还是达不到完全均衡负载,为什么不能像前面rps那样,

echo ff > /proc/irq/<number>/smp_affinity

设置一个ff达到所有的cpu一起均衡呢,这个因为io-apic工作的2个模式logical/low priority跟fixed/physical模式,这两个模式的区别在于,前面一个模式能够把网卡中断传递给多核cpu进行处理,后一种模式对应一个网卡队列的中断只能传递给单cpu进行处理,而linux是fixed/physical工作模式,如果你设置上面那个选项,只能第一个cpu进行软中断的处理,又回到未优化前了。那么为什么不开启logical/low priority呢,当一个tcp连接发起,当数据包到底网卡,网卡触发中断,中断请求到其中一个cpu,而logical/lowpriority并不能保证后续的数据包跟前面的包处于同一个cpu,这样后面的数据包发过来,又可能处于另外一个cpu,这个时候同一个socket都得检查自己的cpu的cache,这样就有可能部分cpu取不到数据,因为本身它的cache并没用数据,这个时候就多了多次的cpu的查找,不能充分利用cpu的效率。对于部分机器来说并不能开启logical/low priority模式,一种可能是cpu过多,另外一种是bios不支持。因此对于那种单队列网卡并不能充分发挥cpu的性能。
经过上述的调整基本可以达到几乎完全均衡的效果,每个cpu都能发挥他的效果。也几乎可以到达我调优的效果
对于一个完整的系统来说,不仅有数据包发送的需求还有数据接收的请求,而rps/rfs主要解决数据接收的一个中断均衡的问题,rps/rfs的作者提交了一个xps(Transmit Packet Steering), 这个patch主要是针对多队列的网卡发送时的优化,当发送一个数据包的时候,它会根据cpu来选择对应的队列,目前这个patch已经添加在2.6.38内核版本当中,我们已经在生产环境中,部分机器上已经使用上了,据作者的benchmark,能够提高20%的性能,具体使用方法

echo ff > /sys/class/net/<interface>/queues/tx-<number>/xps_cpus

由于还是新上的系统,还没敢大规模放用户进来,还在测试系统的稳定性,不知道上限具体能到多少,从当前生产环境跑的流量来看,比同等其它的机器,cpu消耗情况,确实要减少一些,流量没有跑上来,效果不是特别的明显,还有待继续测试,得出一个具体的结果。
另外对于intel的网卡的用户,intel有个叫ioat的功能,关于ioat功能大家可以网上查查资料.
而对于centos的用户来说,目前还只是出了6.0的版本,并没有上述功能,要大规模的推广,建议大家编译2.6.38的内核版本,因为2.6.38的版本已经包含了上述几个补丁。编译内核生成内核的rpm包,能快速的在同一批机器上快速部署上去。
以上就是我的对cpu密集型系统的一个优化过程,欢迎大家来讨论。

还没有评论,快来抢沙发!

发表评论

  • 😉
  • 😐
  • 😡
  • 😈
  • 🙂
  • 😯
  • 🙁
  • 🙄
  • 😛
  • 😳
  • 😮
  • emoji-mrgree
  • 😆
  • 💡
  • 😀
  • 👿
  • 😥
  • 😎
  • ➡
  • 😕
  • ❓
  • ❗
  • 70 queries in 0.454 seconds