前些天發現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
補充:有一個檢視中斷(Interrupt)的top風格小工具 itop ,推薦試試。
确認一下主控端上的網卡資訊,發現其運作在單隊列模式下:
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 0 > /proc/irq/45/smp_affinity_list
再通過「top」指令觀察,會發現處理軟中斷的CPU變成了 0 号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無效。
好消息是Linux支援RPS,通俗點來說就是在軟體層面模拟實作硬體的多隊列網卡功能。
首先看看如何配置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 。
做了如上的優化後,我們再運作「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一起分擔軟中斷才對,可實際結果隻有兩個,有知道原因的請賜教,但是不管怎麼說,兩個總好過一個。
此外,因為這是一台Nginx伺服器,是以通過「worker_cpu_affinity」指令可以配置Nginx使用哪些CPU,如此一來我們便可以繞開高負載的CPU,對性能會有一些幫助。