■ 河南 劉京義
編者按:筆者遇到一例服務器異常丟包的問題,一般來說該故障大多是由帶寬被占滿引發(fā)的,但這次的故障卻并不是帶寬引起的,究竟是什么原因呢?
某單位數(shù)據(jù)中心的一臺服務器最近經(jīng)常出現(xiàn)異常丟包的問題。其表現(xiàn)為在訪問量不大的情況下一切正常,但是如果數(shù)據(jù)流量很大時,就會不斷出現(xiàn)丟包的問題,給正常的業(yè)務帶來的很大的影響。
該服務器安裝的是Cent OS 6.X系統(tǒng),使用的是四核的CPU和16GB的內存。既然是丟包故障,很可能是因為帶寬占滿引發(fā)的,但是查看相關的監(jiān)控信息,發(fā)現(xiàn)其使用的千兆網(wǎng)卡上流量才幾百兆每秒,并沒有占滿帶寬,顯然并不是因為該問題引發(fā)的。
登錄到該機上,執(zhí)行“top”命令,在返回信息中的“l(fā)oad average”欄中顯示系統(tǒng)的負荷并不大,在“Kib Mem”欄中顯示內存使用量一般,還有大量的空閑內存,顯然并不是因為系統(tǒng)超負荷運行導致的上述問題。
但 是,在“%Cpu(s):”欄中的“si”項的數(shù)值為39.1,對 于“si”(即time spent servicing software interrupts)來說,表示的是軟中斷占用CPU資源的百分比,這里的數(shù)值明顯過高,上述故障是不是因為中斷異常造成的呢?
因此,執(zhí)行“cat/proc/interrupts”命令,查看系統(tǒng)中斷情況。在返回信息中顯示所有設備的中斷分布參數(shù),其中第一列為具體的中斷 號,從“cpu0”到“cpu3”各列中顯示各CPU核心處理的中斷數(shù)量。在最后一列顯示與中斷號對應的硬件設備名稱,不同的設備使用的中斷號是不同的。例如,時鐘的中斷號為0,鍵盤中斷號為1,硬盤中斷號為15,網(wǎng)卡中斷號為16等。并且中斷是具有優(yōu)先級的,中斷號越小其擁有的優(yōu)先級越高。
從網(wǎng)卡“eth0”對應的中斷分布情況來看,從系統(tǒng)啟動至今CPU1處理的中斷數(shù)量最多,達到幾百萬次。但是其他幾個CPU核心處理的中斷則很少,甚至為0,這說明多核CPU對于網(wǎng)卡中斷的處理是不均衡的。
對于不同的計算機硬件來說,其和CPU進行通訊實際上是通過中斷實現(xiàn)的。例如當網(wǎng)卡接收到數(shù)據(jù)包時,就會產(chǎn)生中斷信號給CPU,CPU就會中斷當前的工作,通知系統(tǒng)內核有新的數(shù)據(jù)包到來,內核就會調用中斷處理程序加以響應,將數(shù)據(jù)包從網(wǎng)卡緩存區(qū)中復制到內存進行處理。
如果沒有中斷機制,那么當網(wǎng)卡緩沖區(qū)發(fā)生溢出時,就會出現(xiàn)數(shù)據(jù)包被丟棄的問題。CPU利用中斷號來區(qū)分不同的硬件,計算機中的不同硬件擁有不同的中斷號。中斷其實就是一種電信號,由硬件產(chǎn)生并發(fā)送到中斷控制器上。中斷分為硬中斷和軟中斷,前者是由硬件主動產(chǎn)生,處理速度很快,可以通過CPU屏蔽位進行屏蔽。后者是由軟件發(fā)送給系統(tǒng)內核的中斷信號,響應速度較慢,屬于指令方式不能屏蔽。
執(zhí)行“mpstat -P ALL 2”命令,按照每隔兩秒的頻率查看所有CPU核心的狀態(tài)信息,在其中的“CPU”列中顯示所有的CPU核心數(shù),在“%soft”列中顯示對應CPU核心處理的軟中斷數(shù)量,在“%irq”列中顯示其處理的硬中斷數(shù)量??梢钥吹街挥蠧PU1在忙于處理軟中斷,其余的CPU核心則處于則比較空閑。因為網(wǎng)卡的中斷號為16,因此執(zhí)行“cat/proc/irq/16/smp_affinity”命令,查看中斷親緣性配置信息。
其對應的參數(shù)為“smp_affinity”,顯示的數(shù)值為2,其使用的是十六進制,對應的二進制為“0010”,對應的就是CPU1。如果顯示為1/4/8/10/20/40/80的話,對應的是CPU0/2/4/5/6/7/8等。與“smp_affinity”參數(shù)關聯(lián)還有“smp_affinity_list”,其采用的是十進制。兩者的配置信息起著相同的作用。
例如,執(zhí)行“echo 1/proc/irq/16/smp_affinity_list”命令,可以激活CPU0核心處理軟中斷。再次執(zhí)行“mpstat -P ALL 2”命令,可以看到CPU0已經(jīng)開始處理大量的中斷信息了。
根據(jù)以上分析,對于多核CPU來說,在處理網(wǎng)卡中斷時其實并沒有發(fā)揮真正的作用,僅僅是其中某個CPU核心在應對大量的中斷請求。
這對于一般的應用場景來說問題不大,但是對于高流量的服務器來說,就顯示難堪重負了。因為高性能的服務器需要將不同的網(wǎng)卡隊列綁定到不同的CPU核心上,這樣可以將網(wǎng)卡數(shù)據(jù)包產(chǎn)生的中斷負載均衡的分布到不同的CPU核心上,避免出現(xiàn)某個CPU核心忙碌,其它核心閑置的情況,來盡可能的提高多核CPU處理中斷請求的能力,提高服務器整體的數(shù)據(jù)吞吐能力。
上述故障就是因為只有某個CPU核心處理網(wǎng)卡中斷,無法有效應對大量的網(wǎng)卡數(shù)據(jù)包所產(chǎn)生的中斷請求,數(shù)據(jù)包無法全部復制到內存加以處理,造成網(wǎng)卡緩沖區(qū)溢出引發(fā)丟包故障。
解決的方法是,讓所有的CPU核心都參與處理網(wǎng)卡中斷請求,執(zhí)行“echo f/proc/irq/16/smp_affinity”命令,讓4個CPU核心全部都用來處理網(wǎng)卡中斷,因為“f”的二進制值為“1111”,表示啟用4個CPU核心。
注意,對于單隊列網(wǎng)卡來說,僅僅執(zhí)行上述配置其實是沒有效果的。
在CentOS6.X及其以上系統(tǒng)中,內置了RPS(Receive Packet Steering)和RFS(Receive Flow Steering)功能,可以在軟件層面模擬硬件的多隊列網(wǎng)卡功能。RPS可以對網(wǎng)卡接收包中斷中斷進行優(yōu)化,將一個或者多個隊列中的軟中斷軟中斷分布到多個CPU核心上。RFS可以應對應用程序所在的CPU核心和中斷處理的CPU核心不一致的情況。
將兩者分配到同一個CPU核心上。執(zhí)行“echo f/sys/class/net/eth0/queues/rx-0/rps_cpus”命令,來配置RPS功能。
執(zhí)行“sysctl net.core.rps_sock_flow_entries=32768”和“echo 32768/sys/class/net/eth0/queues/rx-0/rps_flow_cnt”命令。配置系統(tǒng)相關核心參數(shù),這里采用的是默認值。當然,網(wǎng)卡的名稱需要根據(jù)實際情況更改。再次執(zhí)行“mpstat -P ALL 2”命令,可以看到所有的CPU核心都開始處理網(wǎng)卡中斷請求了。當然,也可以使用Irqbalance服務,來將中斷平均分布到雖有的CPU核心上,可以有效提升系統(tǒng)性能。
執(zhí)行“rpm -qa | grep irqbalance”命令,來 查看該服務安裝情況。如果沒有安裝,可以執(zhí)行“yum search irqbalance”和“yum install irqbalance”命令,來安裝該服務。執(zhí)行“service irqbalance start”命令,啟動該服務。執(zhí)行“ps -ef|grep irqbalance”命令,查看其運行情況。該服務可以自動將中斷分布到所有的CPU核心上,來有效均衡中斷的處理操作。
如果使用的是多隊列網(wǎng)卡的話,那么就擁有了硬件 的RSS(Receive Side Scaling)功能。它可以根據(jù)網(wǎng)卡的硬件隊列數(shù)量信息,將各隊列的中斷分布到多個CPU核心上。
注意,多隊列網(wǎng)卡需要對應的驅動支持。執(zhí)行“l(fā)spci-vvv”命令,如果返回信息中的“Ethernet controller”欄中存在“MSI-X,Enable+”之類的信息,在其后的“Count=”中數(shù)量大于1的話,就說明網(wǎng)卡支持多隊列功能。
之后執(zhí)行“grep eth0/proc/interrupts |awk'{print $NF}'”命 令,可顯示網(wǎng)卡網(wǎng)卡支持的隊列信息,例如從“eth0-TxRx-0”,“eth0-TxRx-1”到“eth0-TxRx-7”等。執(zhí)行“grep eth0-TxRx/proc/interrupts |awk '{print$1,$NF}'”命 令,顯示 各隊列對應的中斷號,例如從161到169等。除了使用irqbalance服務將對應的隊列綁定到目標CPU核心外,還可以執(zhí)行以下命令,將不同的隊列綁定到CPU0到CPU4等核心上:
當然,如果是8核甚至更多的CPU的話,只需增加對應的的數(shù)量即可。
例如,可以執(zhí)行“echo 80/proc/irq/169/smp_affinity”命令,將第8個隊列綁定到CPU8核心上。如果已經(jīng)啟用了irqbalance服務,那么手動分配是無效的。
如果網(wǎng)卡隊列和CPU核心數(shù)量不匹配,例如CPU是16核的,網(wǎng)卡隊列數(shù)量為8,將隊列分別綁定CPU0到7核心,雖然實現(xiàn)了對網(wǎng)卡中斷的均衡處理,但是其余的CPU核心依然處于閑置狀態(tài)。為此可以采用軟件方式加以處理。
執(zhí)行:
并以此類推直到執(zhí)行“echo ffff/sys/class/net/eth0/queues/rx-7/rps_cpus”命令,將8個網(wǎng)卡隊列全部綁定到所有的CPU核心上,其中的“ffff”表示16核CPU,命令執(zhí)行8次,對應的接收隊列名稱從“rx-0”到“rx-7”。
執(zhí) 行“sysctl net.core.rps_sock_flow_entries=32768”命令,配置RFS參數(shù),來增加CPU緩存命中率,減少網(wǎng)絡延遲。執(zhí)行“l(fā)s/sys/class/net/eth0/queues/rx-*|grep queues|wc -l”命令,顯示接收隊列的數(shù)量。系統(tǒng)系統(tǒng)默認的“rps_sock_flow_entries”參數(shù)表示系統(tǒng)期望的同一時間的活躍連接數(shù),默認值為“32768”。一般來說,它需要除以網(wǎng)卡接收隊列數(shù),來得到“rps_flow_cnt”參數(shù)的值,該參數(shù)和RFS密切相關。這里假設為8個接收隊列,所以其值為4096。
執(zhí)行以下命令:
并以此類推,直到執(zhí)行“echo 4096/sys/class/net/eth0/queues/rx-7/rps_flow_cnt”命令,分別設置所有接收隊列中“rps_flow_cnt”參數(shù)的值。這樣,就可以將網(wǎng)卡所有接收隊列綁定到16個CPU核心上,讓所有的CPU核心都可以處理網(wǎng)卡中斷。
當然,這里僅僅是舉例說明,具體的網(wǎng)卡名稱和隊列名稱需要根據(jù)實際情況來定。