精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

從STGW流量下降探秘內核收包機制

企業動態
在STGW現網運營中,出現了一起流量突然下降的Case,此時我們的健康撥測機制探測到失敗,并且用戶側重試次數增多、請求延遲增大。

 問題現象

在STGW現網運營中,出現了一起流量突然下降的Case,此時我們的健康撥測機制探測到失敗,并且用戶側重試次數增多、請求延遲增大。但通過已有的各類監控進行定位,只發現整體CPU、內存、進程狀態、QPS(每秒請求數)等關鍵指標雖然出現波動,但均未超過告警水位。

[[313051]]

如圖,流量出現了跌幅,并且出現健康檢查撥測失敗。

 

但是,整體CPU在流量出現缺口的期間,并未超過閾值,反而有一些下降,隨后因為恢復正常流量沖高才出現一個小毛刺。

 

此外,內存和應用層監控,都沒有發現明顯異常。

前期探索

顯然,僅憑這些常規監控,無法定位問題根本原因,盡量拿到更多的問題信息,成為了當務之急。幸運的是,從STGW自研的秒級監控系統中,我們查到了一些關鍵的信息。

在STGW自研的監控系統里,我們增加了核心資源細粒度監控,針對CPU、內存、內核網絡協議棧這些核心指標支持秒級監控、監控指標更細化,如下圖就是出問題時間段,cpu各個核心的秒級消耗情況。

 

通過STGW CPU細粒度監控展示的信息,可以看到在出現問題的時間段內,部分CPU核被跑滿,并且是由于軟中斷消耗造成,回溯整個問題時間段,我們還發現,在一段長時間內,這種軟中斷熱點偏高都會在幾個固定的核上出現,不會轉移給其他核。

此外,STGW的監控模塊支持在出現系統核心資源異常時,抓取當時的函數調用棧信息,有了函數調用信息,我們能更準確的知道是什么造成了系統核心資源異常,而不是繼續猜想。如圖展示了STGW監控抓到的函數調用及cpu占比信息:

 

通過函數棧監控信息,我們發現了inet_lookup_listener函數是當時CPU軟中斷熱點的主要消耗者。出現問題時,其他函數調用在沒有發生多少變化情況下,inet_lookup_listener由原本很微小的cpu消耗占比,一下子沖到了TOP1。

通過這里,我們可以初步確定,inet_lookup_listener消耗過高跟軟中斷熱點強相關,當熱點將cpu單核跑滿后就可能引發出流量有損的問題。由于軟中斷熱點持續在產生,線上穩定性隱患很大。基于這個緊迫的穩定性問題,我們從為什么產生熱點、為什么熱點只在部分cpu core上出現兩個方向,進行了問題分析、定位和解決。

為什么產生了熱點

1. 探秘 inet_lookup_listener

由于perf已經給我們提供了熱點所在,首先從熱點函數入手進行分析,結合內核代碼得知,__inet_lookup系列函數是用于將收到的數據包定位到一個具體的socket上,但只有握手包會進入到找__inet_lookup_listener的邏輯,大部分數據包是通過__inet_lookup_established尋找socket。

具體分析lookup_listener的代碼我們發現,由于listen socket不具備四元組特征,因此內核只能用監聽端口計算一個哈希值,并使用了 listening_hash 哈希桶存起來,握手包發過來的時候,就從該哈希桶中尋找對應的listen socket。

  1. struct sock *__inet_lookup_listener(struct net *net, 
  2.             struct inet_hashinfo *hashinfo, 
  3.             const __be32 saddr, __be16 sport, 
  4.             const __be32 daddr, const unsigned short hnum, 
  5.             const int dif) 
  6. // 省略了部分代碼 
  7. // 獲取listen fd 哈希桶 
  8.   struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; 
  9.         result = NULL
  10.   hiscore = 0; 
  11. // 遍歷桶中的節點 
  12.         sk_nulls_for_each_rcu(sk, node, &ilb->head) { 
  13.     score = compute_score(sk, net, hnum, daddr, dif); 
  14.     if (score > hiscore) { 
  15.       result = sk; 
  16.       hiscore = score; 
  17.       reuseport = sk->sk_reuseport; 
  18.       if (reuseport) { 
  19.         phash = inet_ehashfn(net, daddr, hnum, 
  20.                  saddr, sport); 
  21.         matches = 1; 
  22.       } 
  23.     } else if (score == hiscore && reuseport) { 
  24.       matches++; 
  25.       if (((u64)phash * matches) >> 32 == 0) 
  26.         result = sk; 
  27.       phash = next_pseudo_random32(phash); 
  28.     } 
  29.   } 
  30. }  

相對來說并不復雜的lookup_listener函數為什么會造成這么大的cpu開銷?經過進一步定位后,發現問題所在:listen哈希桶開的太小了,只有32個。

  1. /* This is for listening sockets, thus all sockets which possess wildcards. */ 
  2. #define INET_LHTABLE_SIZE  32  /* Yes, really, this is all you need. */ 

為什么內核這里會覺得listen哈希桶大小32就滿足需要了呢?

在IETF(互聯網工程任務組)關于端口的規劃中,0-1023是System port,系統保留使用,1024-49151為Registered port,為IANA(互聯網數字分配機構)可分配給一些固定應用,49152-65535是Dynamic port,是可以真正自由使用的。當然了,這只是IETF的一個規劃,在Linux中,除了System port,另兩個端口段并未真的做了明顯區分,除非端口已經被占用,用戶可以自由使用,這里提一個Linux中跟端口劃分有關聯的內核參數:ip_local_port_range,它表示系統在建立TCP/UDP連接時,系統給它們分配的端口范圍,默認的ip_local_port_range值是32768-60999,進行這個設置后好處是,61000~65535端口是可以更安全的用來做為服務器監聽,而不用擔心一些TCP連接將其占用。

因此,在正常的情況下,服務器的listen port數量,大概就是幾w個這樣的量級。這種量級下,一個port對應一個socket,哈希桶大小為32是可以接受的。然而在內核支持了reuseport并且被廣泛使用后,情況就不一樣了,在多進程架構里,listen port對應的socket數量,是會被幾十倍的放大的。以應用層監聽了5000個端口,reuseport 使用了50個cpu核心為例,5000*50/32約等于7812,意味著每次握手包到來時,光是查找listen socket,就需要遍歷7800多次。隨著機器硬件性能越來越強,應用層使用的cpu數量增多,這個問題還會繼續加劇。

正因為上述原因,并且我們現網機器開啟了reuseport,在端口數量較多的機器里,inet_lookup_listener的哈希桶大小太小,遍歷過程消耗了cpu,導致出現了函數熱點。

2. 如何解決__inet_lookup_listener問題

Linux社區難道沒有注意到開啟reuseport后,原來的哈希桶大小不夠用這個問題嗎?

其實社區是注意到了這個問題的,并且有修復這個問題。

從Linux 4.17開始,Linux社區就修復了由于reuseport帶來的socket數量過多,導致inet_lookup_listener查找緩慢的問題,修復方案分兩步:

1. 引入了兩次查找,首先還是根據目的端口進行哈希,接著會使用握手包中拿到的四元組信息,按照四元組進行第一次查找,如果四元組獲取不到結果,則使用之前那種對于任意IP地址查找。

  1. struct sock *__inet_lookup_listener(struct net *net, 
  2.             struct inet_hashinfo *hashinfo, 
  3.             struct sk_buff *skb, int doff, 
  4.             const __be32 saddr, __be16 sport, 
  5.             const __be32 daddr, const unsigned short hnum, 
  6.             const int dif, const int sdif) 
  7.   struct inet_listen_hashbucket *ilb2; 
  8.   struct sock *result = NULL
  9.   unsigned int hash2; 
  10.  
  11. // 根據目的端口進行第一次哈希 
  12.   hash2 = ipv4_portaddr_hash(net, daddr, hnum); 
  13.   ilb2 = inet_lhash2_bucket(hashinfo, hash2); 
  14. // 根據四元組信息再做一次查找 
  15.   result = inet_lhash2_lookup(net, ilb2, skb, doff, 
  16.             saddr, sport, daddr, hnum, 
  17.             dif, sdif); 
  18.   if (result) 
  19.     goto done; 
  20.  
  21.   /* Lookup lhash2 with INADDR_ANY */ 
  22. // 四元組沒查到,嘗試在0.0.0.0監聽范圍查找 
  23.   hash2 = ipv4_portaddr_hash(net, htonl(INADDR_ANY), hnum); 
  24.   ilb2 = inet_lhash2_bucket(hashinfo, hash2); 
  25.  
  26.   result = inet_lhash2_lookup(net, ilb2, skb, doff, 
  27.             saddr, sport, htonl(INADDR_ANY), hnum, 
  28.             dif, sdif); 
  29. done: 
  30.   if (IS_ERR(result)) 
  31.     return NULL
  32.   return result; 

2. 合并處理reuseport放大出來的socket,在發現指定的端口開啟了reuseport后,不再是遍歷式的去獲取到合適的socket,而是將其看成一個整體,二次哈希后,調用 reuseport_select_sock,取到合適的socket。

  1. static struct sock *inet_lhash2_lookup(struct net *net, 
  2.         struct inet_listen_hashbucket *ilb2, 
  3.         struct sk_buff *skb, int doff, 
  4.         const __be32 saddr, __be16 sport, 
  5.         const __be32 daddr, const unsigned short hnum, 
  6.         const int dif, const int sdif) 
  7.   bool exact_dif = inet_exact_dif_match(net, skb); 
  8.   struct inet_connection_sock *icsk; 
  9.   struct sock *sk, *result = NULL
  10.   int score, hiscore = 0; 
  11.   u32 phash = 0; 
  12.  
  13.   inet_lhash2_for_each_icsk_rcu(icsk, &ilb2->head) { 
  14.     sk = (struct sock *)icsk; 
  15.     score = compute_score(sk, net, hnum, daddr, 
  16.               dif, sdif, exact_dif); 
  17.     if (score > hiscore) { 
  18.       if (sk->sk_reuseport) { 
  19.                 // 如果是reuseport,進行二次哈希查找 
  20.         phash = inet_ehashfn(net, daddr, hnum, 
  21.                  saddr, sport); 
  22.         result = reuseport_select_sock(sk, phash, 
  23.                      skb, doff); 
  24.         if (result) 
  25.           return result; 
  26.       } 
  27.       result = sk; 
  28.       hiscore = score; 
  29.     } 
  30.   } 
  31.  
  32.   return result; 

總結來說,社區通過引入兩次查找+合并reuseport sockets的處理,解決了reuseport帶來的sockets數量放大效果。這里結合我們的探索,另外提供兩個可行的低成本解決方案:

1. 修改內核哈希桶大小,根據reuseport增加socket的倍數,相應提高INET_LHTABLE_SIZE,或者直接改成例如2048

  1. #define INET_LHTABLE_SIZE  2048  

2. 關閉reuseport可以減少socket數目到32個哈希桶大小能承受的范圍,從而降低該函數消耗。

加上社區方案,這里的三個方法在本質上都是減少listen table哈希桶的遍歷復雜度。社區的方案一套比較系統的方法,今后隨著內核版本升級,肯定會將這個問題解決掉。但短期升級內核的成本較高,所以后面兩個方案就可以用來短期解決問題。此外,關閉reuseport雖然不需要更改內核,但需要考慮應用層server對于reuseport的依賴情況。

為什么熱點只在部分核心出現

解決完哈希桶問題后,我們并沒有定位到全部的問題,前面提到,軟中斷熱點僅在部分cpu核上出現,如果僅僅是__inet_lookup_listener問題,按理所有cpu核的軟中斷消耗都會偏高。如果這里問題沒有解釋清楚,一旦出現熱點函數,一些單核就會被跑滿,意味著整機容量強依賴部分單核的性能瓶頸,超出了單核能力就會有損,這是完全不能接受的。

1. 從CPU中斷數入手

根據問題現象,我們做了一些假設,在這里最直觀的假設就是,我們的數據包在各個核上并不是負載均衡的。

首先,通過cat /proc/interrupts找到網卡在各個cpu核的中斷數,發現網卡在各個核的硬中斷就已經不均衡了。那么會是硬中斷親和性的問題嗎?接著檢查了網卡各個隊列的smp_affiinity,發現每個隊列與cpu核都是一一對應并且互相錯開,硬中斷親和性設置沒有問題。

緊接著,我們排查了網卡,我們的網卡默認都打開了RSS(網卡多隊列),每個隊列綁定到一個核心上,既然硬中斷親和性沒有問題,那么會是網卡多隊列本身就不均衡嗎?通過ethtool -S eth0/eth1再過濾出每個rx_queue的收包數,我們得到如下圖:

 

原來網卡多隊列收包就已經嚴重不均衡了,以入包量升序排序,發現不同的rx_queue 收包數量相差達到了上萬倍!

2. 探究網卡多隊列(RSS)

這里我們著重檢查了幾個網卡多隊列的參數

  1. // 檢查網卡的隊列數 
  2. ethtool -l eth0 
  3. Current hardware settings: 
  4. RX:             0 
  5. TX:             0 
  6. Other:          1 
  7. Combined:       48 
  8.  
  9. // 檢查硬件哈希開關 
  10. ethtool -k eth0 
  11. receive-hashing: on 
  12.  
  13. // 檢查硬件哈希的參數,這里顯示以TCP是以四元組信息進行哈希 
  14. ethtool -n eth0 rx-flow-hash tcp4 
  15. TCP over IPV4 flows use these fields for computing Hash flow key
  16. IP SA 
  17. IP DA 
  18. L4 bytes 0 & 1 [TCP/UDP src port] 
  19. L4 bytes 2 & 3 [TCP/UDP dst port] 

這些參數都是符合預期的,數據包會根據TCP包的四元組哈希到不同的隊列上。我們繼續使用假設論證法,會是數據包本身就是比如長連接,導致不均衡嗎?通過檢查我們服務端的日志,發現請求的ip和端口都是比較分散的,傳輸的數據也都是較小文件,并沒有集中化。

經過了一番折騰,我們有了新的假設,由于我們現網大部分流量是IPIP隧道及GRE封裝的數據包,在普通數據包的IP header上多了一層header,外層IP與我們在server看到的并不一樣,外層IP是非常集中的。這里是否會讓網卡多隊列的均衡策略失效呢?

來源網圖,以GRE包為例,IP數據包其實是分了外層IP頭部、gre層、內層IP頭部,以及再往上的TCP/UDP層,如果只獲取了外層IP頭部,則較為集中,難以進行分散。

 

經過同事幫忙牽線,我們從網卡廠商處獲得了重要的信息,不同的網卡對于多隊列哈希算法是不一樣的!

從網卡廠商處進一步確認得知,我們在使用的這款網卡,是不支持解析封裝后的數據包的,只會以外層IP作為哈希依據。廠商提供了一款新型號的網卡,是支持解析IPIP及GRE內層IP PORT的。我們經過實測這兩種網卡,發現確實如此。

看到這里,網卡多隊列不均衡問題原因已經定位清楚,由于現網使用了IPIP或GRE這類封裝協議,部分網卡不支持解析內層IP PORT進行哈希,從而導致多隊列不均衡,進一步導致cpu硬中斷不均衡,然后不均衡的軟中斷熱點便出現了。

3. 如何解決網卡多隊列不均衡

對于STGW來說,我們已經確定了不均衡的網卡型號,都是型號較老的網卡,我們正在逐步使用新的網卡型號,新網卡型號已驗證支持IPIP及GRE格式的數據包負載均衡。

為什么RPS沒有起作用

Receive Packet Steering (RPS),是內核的一種負載均衡機制,即便硬件層面收到的數據包不均衡的,RPS會對數據包再次進行哈希與分流,保證其進入網絡協議棧是均衡的。

經過確認,出問題機器上都開啟了RPS。所以問題還是沒有解釋清楚,即便舊型號的網卡RSS不均衡,但經過內核RPS后,數據包才會送給網絡協議棧,然后調用_inet_lookup_listener,此時依然出現熱點不均衡,說明RPS并未生效。

1. 了解硬件及內核收包流程

由于引入了RPS這個概念,在定位該問題前,我梳理了一份簡明收包流程,通過了解數據包是如何通過硬件、內核、再到內核網絡協議棧,可以更清晰的了解RPS所處的位置,以及我們遇到的問題。

 

如上圖所示,數據包在進入內核IP/TCP協議棧之前,經歷了這些步驟:

  1. 網口(NIC)收到packets
  2. 網口通過DMA(Direct memeory access)將數據寫入到內存(RAM)中。
  3. 網口通過RSS(網卡多隊列)將收到的數據包分發給某個rx隊列,并觸發該隊列所綁定核上的CPU中斷。
  4. 收到中斷的核,調用該核所在的內核軟中斷線程(softirqd)進行后續處理。
  5. softirqd負責將數據包從RAM中取到內核中。
  6. 如果開啟了RPS,RPS會選擇一個目標cpu核來處理該包,如果目標核非當前正在運行的核,則會觸發目標核的IPI(處理器之間中斷),并將數據包放在目標核的backlog隊列中。
  7. 軟中斷線程將數據包(數據包可能來源于第5步、或第6步),通過gro(generic receive offload,如果開啟的話)等處理后,送往IP協議棧,及之后的TCP/UDP等協議棧。

回顧我們前面定位的問題,__inet_lookup_listener熱點對應的是IP協議棧的問題,網卡多隊列不均衡是步驟3,RSS階段出現的問題。RPS則是在步驟6中。

2. 探秘RPS負載不均衡問題

通過cat /proc/net/softnet_stat,可以獲取到每個核接收的RPS次數。拿到這個數目后,我們發現,不同的核在接收RPS次數上相差達到上百倍,并且RPS次數最多的核,正好就是軟中斷消耗出現熱點的核。

至此我們發現,雖然網卡RSS存在不均衡,但RPS卻依然將過多的數據包給了部分cpu core,沒有做到負載均衡,這才是導致我們軟中斷熱點不均衡的直接原因。

通過在內核代碼庫中找到RPS相關代碼并進行分析,我們再次發現了一些可疑的點

  1. static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, 
  2.            struct rps_dev_flow **rflowp) 
  3. // 省略部分代碼 
  4.   struct netdev_rx_queue *rxqueue; 
  5.   struct rps_map *map; 
  6.   struct rps_dev_flow_table *flow_table; 
  7.   struct rps_sock_flow_table *sock_flow_table; 
  8.   int cpu = -1; 
  9.   u16 tcpu; 
  10.  
  11.   skb_reset_network_header(skb); 
  12.   if (rps_ignore_l4_rxhash) { 
  13. // 計算哈希值 
  14.     __skb_get_rxhash(skb); 
  15.     if (!skb->rxhash) 
  16.       goto done; 
  17.   } 
  18.   else if(!skb_get_rxhash(skb)) 
  19.     goto done; 
  20.  
  21. // 通過哈希值計算出目標CPU 
  22.         if (map) { 
  23.     tcpu = map->cpus[((u64) skb->rxhash * map->len) >> 32]; 
  24.  
  25.     if (cpu_online(tcpu)) { 
  26.       cpu = tcpu; 
  27.       goto done; 
  28.     } 
  29.   } 
  30.  
  31. done: 
  32.   return cpu; 
  33.  
  1. /* 
  2.  * __skb_get_rxhash: calculate a flow hash based on src/dst addresses 
  3.  * and src/dst port numbers.  Sets rxhash in skb to non-zero hash value 
  4.  * on success, zero indicates no valid hash.  Also, sets l4_rxhash in skb 
  5.  * if hash is a canonical 4-tuple hash over transport ports. 
  6.  */ 
  7. void __skb_get_rxhash(struct sk_buff *skb) 
  8.   struct flow_keys keys; 
  9.   u32 hash; 
  10.  
  11.   if (!skb_flow_dissect(skb, &keys)) 
  12.     return
  13.  
  14.   if (keys.ports) 
  15.     skb->l4_rxhash = 1; 
  16. // 使用TCP/UDP四元組進行計算 
  17.   /* get a consistent hash (same value on both flow directions) */ 
  18.   if (((__force u32)keys.dst < (__force u32)keys.src) || 
  19.       (((__force u32)keys.dst == (__force u32)keys.src) && 
  20.        ((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) { 
  21.     swap(keys.dst, keys.src); 
  22.     swap(keys.port16[0], keys.port16[1]); 
  23.   } 
  24. // 使用jenkins哈希算法 
  25.   hash = jhash_3words((__force u32)keys.dst, 
  26.           (__force u32)keys.src, 
  27.           (__force u32)keys.ports, hashrnd); 
  28.   if (!hash) 
  29.     hash = 1; 
  30.  
  31.   skb->rxhash = hash; 

猜想一:rps_ignore_l4_rxhash未打開,導致不均衡?

通過代碼發現 rps_ignore_l4_rxhash 會影響當前是否計算哈希值,當前機器未設置ignore_l4_rxhash,則內核會直接使用網卡RSS計算出的哈希值,根據上面定位的網卡RSS不均衡的結論,RSS哈希值可能是不準的,這里會導致問題嗎?

我們將ignore_l4_rxhash開關進行打開

  1. sysctl -w kernel.rps_ignore_l4_rxhash=1 

發現并沒有對不均衡問題產生任何改善,排除這個假設。

猜想二:RPS所使用的哈希算法有缺陷,導致不均衡?

對于負載不均衡類的問題,理所應當會懷疑當前使用的均衡算法存在缺陷,RPS這里使用的是jenkins hash(jhash_3words)算法,是一個比較著名且被廣泛使用的算法,經過了很多環境的驗證,出現缺陷的可能性較小。但我們還是想辦法進行了一些驗證,由于是內核代碼,并且沒有提供替代性的算法,改動內核的代價相對較高。

因此這里我們采取的對比的手段快速確定,在同樣的內核版本,在現網找到了負載均衡的機器,檢查兩邊的一些內核開關和RPS配置都是一致的,說明同樣的RPS哈希算法,只是部分機器不均衡,因此這里算法側先不做進一步挖掘。

猜想三:和RSS問題一樣,RPS也不支持對封裝后的數據進行四元組哈希?

skb_flow_dissect是負責解析出TCP/UDP四元組,經過初步分析,內核是支持IPIP、GRE等通用的封裝協議,并從這些協議數據包中,取出需要的四元組信息進行哈希。

在各種假設與折騰都沒有找到新的突破之時,我們使用systemtap這個內核調試神器,hook了關鍵的幾個函數和信息,經過論證和測試后,在現網進行了短暫的debug,收集到了所需要的關鍵信息。

  1. #! /usr/bin/env stap 
  2. /* 
  3. Analyse problem that softirq not balance with RPS. 
  4.  
  5. Author: dalektan@tencent.com 
  6.  
  7. Usage: 
  8. stap -p4 analyse_rps.stp -m stap_analyse_rps 
  9. staprun -x cpuid stap_analyse_rps.ko 
  10. */ 
  11.  
  12.  
  13. // To record how cpu changed with rps execute 
  14.  
  15. private global target_cpu = 0 
  16. private global begin_cpu 
  17. private global end_cpu 
  18.  
  19. probe begin { 
  20.   target_cpu = target() 
  21.   begin_cpu = target_cpu - 2 
  22.   end_cpu = target_cpu + 2 
  23. // 指定需要分析的cpu范圍,避免對性能產生影響 
  24.   printf("Prepare to analyse cpu is :%d-%d\n", begin_cpu, end_cpu) 
  25.  
  26.  
  27. // To record tsv ip addr, daddr and protocol(ipip, gre or tcp) 
  28. probe kernel.function("ip_rcv").call { 
  29.   if (cpu() >= begin_cpu && cpu() <= end_cpu) { 
  30.     ip_protocol = ipmib_get_proto($skb) 
  31.     // if not tcp, ipip, gre, then return 
  32.     if (ip_protocol == 4 || ip_protocol == 6 || ip_protocol == 47) { 
  33.       saddr = ip_ntop(htonl(ipmib_remote_addr($skb, 0))) 
  34.       daddr = ip_ntop(htonl(ipmib_local_addr($skb, 0))) 
  35.  
  36.     printf("IP %s -> %s proto:%d rx_queue:%d cpu:%d\n"
  37.            saddr, daddr, ip_protocol, $skb->queue_mapping-1, cpu()) 
  38.     } 
  39.   } 
  40.  
  41. // To record tcp states 
  42. probe tcp.receive.call { 
  43.   if (cpu() >= begin_cpu && cpu() <= end_cpu) { 
  44.      printf("TCP %s:%d -> %s:%d  syn:%d  rst:%d  fin:%d cpu:%d\n"
  45.             saddr, sport , daddr, dport, syn, rst, fin, cpu()) 
  46.   } 

通過使用上述systemtap腳本進行分析后,我們得到了一個關鍵信息,大量GRE協議(圖中proto:47)的數據包,無論其四元組是什么,都被集中調度到了單個核心上,并且這個核心正好是軟中斷消耗熱點核。并且其他協議數據包未出現這個問題。

 

走到這里,問題漸為開朗,GRE數據包未按預期均衡到各個核心,但根據之前的分析,RPS是支持GRE協議獲取四元組的,為什么在這里,不同的四元組,依然被哈希算成了同一個目標核呢?

3. 探究GRE數據包不均衡之謎

帶著這個問題進一步挖掘,通過抓包以及代碼比對分析,很快有了突破,定位到了原因是:當前內核僅識別GRE_VERSION=0的GRE協議包并獲取其四元組信息,而我們的數據包,是GRE_VERSION=1的。

  1. // skb_flow_dissect 獲取四元組信息 
  2. switch (ip_proto) { 
  3.   case IPPROTO_GRE: { 
  4.     struct gre_hdr { 
  5.       __be16 flags; 
  6.       __be16 proto; 
  7.     } *hdr, _hdr; 
  8.  
  9.     hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); 
  10.     if (!hdr) 
  11.       return false
  12.     /* 
  13.      * Only look inside GRE if version zero and no 
  14.      * routing 
  15.      */ 
  16. // 只解析GRE_VERSION = 0的GRE協議數據包 
  17.     if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) { 
  18.       proto = hdr->proto; 
  19.       nhoff += 4; 
  20.       if (hdr->flags & GRE_CSUM) 
  21.         nhoff += 4; 
  22.       if (hdr->flags & GRE_KEY) 
  23.         nhoff += 4; 
  24.       if (hdr->flags & GRE_SEQ) 
  25.         nhoff += 4; 
  26.       if (proto == htons(ETH_P_TEB)) { 
  27.         const struct ethhdr *eth; 
  28.         struct ethhdr _eth; 
  29.  
  30.         eth = skb_header_pointer(skb, nhoff, 
  31.                sizeof(_eth), &_eth); 
  32.         if (!eth) 
  33.           return false
  34.         proto = eth->h_proto; 
  35.         nhoff += sizeof(*eth); 
  36.       } 
  37.       goto again; 
  38.     } 
  39.     break; 
  40.   }  

首先,我們先確認一下,GRE_VERSION=1是符合規范的嗎,答案是符合的,如果去了解一下PPTP協議的話,可以知道RFC規定了PPTP協議就是使用的GRE協議封裝并且GRE_VERSION=1。

那么為什么內核這里不支持這種標記呢?

通過在Linux社區進行檢索,我們發現Linux 4.10版本開始支持PPTP協議下的GRE包識別與四元組獲取,也就是GRE_VERSION=1的情況。由于沒有加入對PPTP協議的支持,因此出現不識別GRE_VERSION=1(PPTP協議)的情況,RPS不會去獲取這種數據包的四元組信息,哈希后也是不均衡的,最終導致單核出現軟中斷熱點。

4. 如何解決RPS不均衡問題

至此,所有的問題都已經撥開云霧,最后對于RPS不均衡問題,這里提供三種解決方案:

  1. 對于RSS網卡多隊列已經是均衡的機器,可以將修改kernel.rps_ignore_l4_rxhash = 0,讓RPS直接使用網卡硬件哈希值,由于硬件哈希值足夠分散,因此RPS效果也是均衡的。
  2. 對于RSS網卡多隊列均衡的機器,通過ethtool -S/-L查看或修改網卡多隊列數目,如果隊列數不少于cpu核數,再將多隊列通過/proc/irq/設備id/smp_affinity分散綁定到不同的cpu核。這樣可以充分利用網卡RSS的均衡效果,而無需打開內核RPS。
  3. 在當前內核進行修改或熱補丁,在RPS函數中新增對GRE_VERSION=1的GRE數據包的識別與四元組獲取。
  4. 升級內核到Linux 4.10之后,即可支持PPTP協議包的RPS負載均衡。

總結

最后,總結一下整個問題和定位過程,我們從一次流量下降,業務有損的問題出發,從最開始找不到思路,到找到軟中斷熱點這個關鍵點,再到區分IP協議棧、網卡多隊列、內核收包這三個層面進行問題入手,沒有滿足于已經解決的部分問題,不斷深挖了下去,終于各個方向擊破,撥開了問題的層層面紗,并給出了解決方案。

借著對問題的定位和解決,收獲良多,學習了內核收包流程,熟悉了內核問題定位工具和手段。感謝STGW組里同事,文中諸多成果都是團隊的共同努力。

【本文為51CTO專欄作者“騰訊技術工程”原創稿件,轉載請聯系原作者(微信號:Tencent_TEG)】

 

戳這里,看該作者更多好文

 

責任編輯:武曉燕 來源: 騰訊技術工程
相關推薦

2010-06-10 10:49:32

openSUSE使用教

2025-10-17 09:24:51

2013-05-13 09:52:52

Windows內核Linux內核

2009-08-25 15:30:55

DataGrid We

2023-06-07 15:25:19

Kafka版本日志

2010-03-29 16:48:18

Nginx內核優化

2014-12-10 11:18:17

搜索社交app運營

2025-10-11 04:11:00

2025-11-03 04:00:00

2021-01-06 09:01:05

javaclass

2025-08-11 02:00:00

2023-11-24 11:24:16

Linux系統

2009-07-09 18:15:42

JDBC事務處理

2016-09-20 15:21:35

LinuxInnoDBMysql

2018-06-26 12:00:09

運營商流量漫游5G

2024-08-12 14:37:38

2010-09-26 14:08:41

Java垃圾回收

2009-10-29 09:41:01

Linux內核DeviceMappe

2017-08-16 16:20:01

Linux內核態搶占用戶態搶占

2019-11-28 09:04:32

DDoS網絡攻擊網絡安全
點贊
收藏

51CTO技術棧公眾號

欧美日韩在线看| 久久电影网站中文字幕| 日韩成人久久久| 国产一区亚洲二区三区| 在线免费观看的av网站| 国产精品性做久久久久久| 777777777亚洲妇女| 9.1片黄在线观看| 91精品尤物| 日本福利一区二区| 成年丰满熟妇午夜免费视频| 欧洲一区av| 紧缚奴在线一区二区三区| 国内精品久久久久久影视8| 能直接看的av| 999久久久久久久久6666| 在线观看日韩高清av| 国产一区二区三区在线免费| 超碰免费在线观看| 成人精品一区二区三区中文字幕| 国产精品扒开腿做爽爽爽的视频| 青青青在线视频| 成人免费在线观看av| 精品国产一区二区三区av性色 | 成人在线视频免费播放| 国产成人福利夜色影视| 福利视频导航一区| 大桥未久一区二区三区| 国产在线你懂得| jlzzjlzz亚洲日本少妇| 亚洲999一在线观看www| 国产精品成人久久久| 国产欧美激情| 欧美国产精品va在线观看| 日本成人精品视频| 国产成人黄色| 日韩精品一二三四区| 久草福利在线观看| 91久久青草| 欧美丝袜丝交足nylons图片| 久久精品一区二| 2021中文字幕在线| 亚洲一二三四在线观看| 超薄肉色丝袜足j调教99| a√在线中文网新版址在线| 91在线高清观看| 精品免费视频123区| 高清毛片aaaaaaaaa片| 国产精品自拍毛片| 成人黄色免费网站在线观看| 在线观看毛片av| 日韩国产精品久久久| 日韩美女免费线视频| 日韩污视频在线观看| 欧美日本精品| 欧美日韩成人在线播放| 欧美成人免费看| 91精品1区| 久久精品中文字幕电影| 欧洲美女女同性互添| 五月精品视频| 免费不卡在线观看av| 91嫩草丨国产丨精品| 亚洲一区在线| 欧美福利视频网站| 日韩和一区二区| av不卡免费看| 日本亚洲欧洲色| 国产情侣免费视频| 日本va欧美va欧美va精品| 国产精品美女www爽爽爽视频| 无码一区二区三区| 日韩av一区二区在线影视| 国产精品免费一区豆花| 亚洲性生活大片| 韩国一区二区三区| 999国产在线| 欧美视频xxx| 91蝌蚪porny| 亚洲一区高清| 色婷婷av在线| 黑人巨大精品欧美一区二区三区 | 不卡av电影在线播放| 国产在线一区二区三区四区| 欧美视频免费一区二区三区| 中文字幕乱码日本亚洲一区二区| 一本二本三本亚洲码| 欧美xxxx少妇| 色综合中文综合网| 久久久久久久久久一区| 91麻豆精品激情在线观看最新| 亚洲国产精品专区久久| 免费人成又黄又爽又色| 国产精品99一区二区三| 国色天香2019中文字幕在线观看| 日韩在线视频不卡| 精品无人码麻豆乱码1区2区| 国产成人aa精品一区在线播放| 欧美精品高清视频| 日韩不卡手机在线v区| 成人免费看视频| 91视频九色网站| 俄罗斯嫩小性bbwbbw| 91一区二区在线| 亚洲蜜桃av| 国产后进白嫩翘臀在线观看视频| 日韩欧美视频一区二区三区| 182午夜在线观看| 好吊妞国产欧美日韩免费观看网站 | 精品国产欧美一区二区| 国精产品一区二区三区| 午夜日韩激情| 国产精品久久久久久久7电影| aaa一区二区| 日本一区二区三区四区| 亚洲熟妇无码一区二区三区| 男人亚洲天堂| 日韩国产欧美精品一区二区三区| 国产男女免费视频| a在线观看视频| 国产肉丝袜一区二区| 福利视频免费在线观看| 99综合99| 国产性猛交xxxx免费看久久| 国产精品成人aaaa在线| 麻豆国产精品官网| 欧美日韩一区二区三区在线观看免 | 免费看日产一区二区三区| 国产一区二区三区高清在线观看| 国产第一页在线播放| 精品一区二区三区蜜桃| 欧洲精品码一区二区三区免费看| 激情图片在线观看高清国产| 91精品国产欧美一区二区| 无码人妻丰满熟妇啪啪欧美| 99精品国产在热久久| 91日韩久久| 黄色av电影在线播放| 欧美三级日本三级少妇99| 亚洲av无码成人精品国产| 亚洲国产清纯| 高清视频在线观看一区| 18av在线播放| 日韩小视频在线观看专区| 国产一二三av| 奇米影视一区二区三区| 四虎永久国产精品| 欧美aaa视频| 亚洲人成电影网站色xx| 狠狠人妻久久久久久| gogo大胆日本视频一区| 国产九色porny| av男人一区| 欧美激情综合亚洲一二区| av免费在线不卡| 一区二区三区四区国产精品| 国产精品91av| 亚洲大胆av| 芒果视频成人app| 久草在线免费福利资源| 免费观看一级视频| 青娱乐国产精品视频| 成人三级视频在线播放| 欧美综合在线播放| 欧日韩免费视频| 分分操这里只有精品| 精品成在人线av无码免费看| 欧美 日韩 国产 在线观看| 亚洲一区二区中文| 韩国成人动漫在线观看| 国模私拍视频在线| 亚洲免费观看高清完整版在线观看| 日韩成人精品视频在线观看| 在线看片不卡| 国产精品一区二区三区精品| 超碰资源在线| 亚洲色图欧美制服丝袜另类第一页 | 色综合视频一区二区三区日韩| 日韩小视频在线| 国产麻豆91视频| 亚洲高清一区二区三区| 熟女俱乐部一区二区视频在线| 日本aⅴ精品一区二区三区| 一区二区在线不卡| 国产suv精品一区| www国产精品av| 亚洲日本青草视频在线怡红院| 色撸撸在线观看| 2020国产精品极品色在线观看| 97久久国产精品| 国产私人尤物无码不卡| 欧美精品在欧美一区二区少妇| 69av视频在线| 久久久久久亚洲综合| а 天堂 在线| 国产视频亚洲| 亚洲欧美日韩不卡一区二区三区| 欧洲大片精品免费永久看nba| 午夜精品视频在线| 91露出在线| 日韩精品一区二区三区老鸭窝 | 国产在线观看一区二区三区 | 久久亚洲国产| 国产精品视频免费观看| 成人精品国产亚洲| 久久乐国产精品| www日韩tube| 日韩av在线网址| av综合在线观看| 色美美综合视频| 久久久久亚洲av无码专区| 久久精品无码一区二区三区| 欧美日韩理论片| 日韩中文字幕一区二区三区| 精品国产一区二区三区无码| 日韩一级毛片| 欧美一区二区三区成人久久片| 一区二区亚洲视频| 91免费欧美精品| 电影一区电影二区| 日本国产欧美一区二区三区| 色呦呦在线资源| 久久香蕉频线观| av影片在线看| 亚洲天堂av电影| 手机亚洲第一页| 亚洲成人网在线| 亚洲AV无码精品自拍| 91精品在线一区二区| 日韩欧美国产另类| 婷婷综合另类小说色区| 日本少妇xxx| 手机在线看片1024| 国产精品自拍av| 国产精品久久国产| 99国产**精品****| 蜜桃91精品入口| 风间由美性色一区二区三区四区 | 成人手机在线播放| 国产午夜一区| 欧洲一区二区在线观看| 国产精品极品在线观看| 91麻豆国产精品| 只有精品亚洲| 国产精品一区二区三区久久| 毛片无码国产| 2019中文字幕全在线观看| 欧美亚洲系列| 97激碰免费视频| 成人国产免费电影| 久久91精品国产| 超碰最新在线| 欧美成人网在线| 手机av在线播放| 欧美丰满少妇xxxx| 牛牛电影国产一区二区| 久久理论片午夜琪琪电影网| 激情av在线播放| 欧美极品欧美精品欧美视频| 欧美xxxxhdvideosex| 欧美乱大交xxxxx| 成人在线观看亚洲| 日韩最新av在线| 午夜在线小视频| 中文字幕在线看视频国产欧美| 婷婷五月综合激情| 日韩国产精品亚洲а∨天堂免| 五月天福利视频| 精品视频中文字幕| 97电影在线| 久久深夜福利免费观看| 成人国产免费电影| 97国产精品免费视频| 成人国产二区| 国产精品久久久久秋霞鲁丝| 韩国精品主播一区二区在线观看 | 久久视频一区| 噼里啪啦国语在线观看免费版高清版| 日日摸夜夜添夜夜添精品视频 | 国产精品一国产精品k频道56| 中国丰满熟妇xxxx性| 亚洲国内精品| 欧美一级黄色片视频| 美腿丝袜亚洲综合| 黄色小视频免费网站| 成人黄色小视频在线观看| 欧美一区二区三区成人精品| 国产欧美一区二区在线观看| 日韩精品123区| 亚洲午夜激情网页| 69成人免费视频| 91精品国产美女浴室洗澡无遮挡| 精品人妻午夜一区二区三区四区 | 菠萝蜜视频国产在线播放| 精品五月天久久| 国产永久免费高清在线观看视频| 日韩中文字幕第一页| 国产一区二区三区三州| 91丨精品丨国产| 日韩毛片在线看| 尤物视频在线免费观看| 欧美人在线观看| 欧美激情网站| 91日本视频在线| 韩国av免费在线| 日韩一区二区免费电影| 天天舔天天干天天操| 久久久精品免费视频| 鲁鲁在线中文| 国产专区精品视频| 国产成人三级| 欧美成人精品免费| 日韩成人伦理电影在线观看| 亚洲女则毛耸耸bbw| 国产调教视频一区| 久久影院一区二区| 欧美日韩精品免费观看视频| 日韩一级片免费在线观看| 亚洲视频在线免费观看| av剧情在线观看| 91精品久久久久久久久久久| 亚州av日韩av| 欧美日韩激情四射| 秋霞电影一区二区| 国产精品扒开腿做爽爽爽a片唱戏 亚洲av成人精品一区二区三区 | 天天夜碰日日摸日日澡性色av| 久久超碰97人人做人人爱| 日韩www视频| 亚洲蜜臀av乱码久久精品| 五月天综合激情网| 精品福利一二区| 国产视频中文字幕在线观看| 欧美专区第一页| 精品久久对白| 9色视频在线观看| 奇米影视在线99精品| 白白色免费视频| 性欧美疯狂xxxxbbbb| 国产一级精品毛片| 亚洲区在线播放| 性欧美18~19sex高清播放| 91精品婷婷国产综合久久蝌蚪| 四季av一区二区三区免费观看| 国产日韩欧美精品在线观看| 国产精品一二三四| 在线看的片片片免费| 欧美高清激情brazzers| 日韩在线免费看| 日本aⅴ大伊香蕉精品视频| 噜噜噜狠狠夜夜躁精品仙踪林| 熟女视频一区二区三区| 精品亚洲免费视频| 国产人与禽zoz0性伦| 欧美日韩在线一区二区| 69视频在线| 国产精品综合久久久| 午夜影院欧美| 亚洲av无日韩毛片久久| 亚洲人成7777| 蜜桃av噜噜一区二区三区麻豆| 中文字幕亚洲无线码a| 国产精品天堂蜜av在线播放| 日本在线免费观看一区| 日韩国产欧美三级| 多男操一女视频| 欧美一区二区视频免费观看| 黄av在线播放| 国产精品对白刺激久久久| 伊人久久大香线蕉av超碰演员| 久久久久亚洲av片无码v| 亚洲国产一二三| 日韩一卡二卡在线| 国产www精品| 久久影视一区| 欧美性受xxxx黒人xyx性爽| 亚洲国产aⅴ成人精品无吗| 天堂网在线播放| 日韩美女写真福利在线观看| 久久精品国产亚洲夜色av网站| av中文字幕网址| 亚洲欧美日韩中文播放 | 香蕉视频在线播放| 成人网在线视频| 欧美三级特黄| 偷拍夫妻性生活| 欧美日韩小视频| caoporn免费在线| 精品中文字幕人| 日韩精品久久久久久| 亚洲精品天堂网| 亚洲国产成人精品电影| 小早川怜子影音先锋在线观看| 性刺激综合网| 粉嫩嫩av羞羞动漫久久久| 五月婷婷中文字幕| 久久中文精品视频| 国产亚洲成av人片在线观黄桃| 99热这里只有精品在线播放| 亚洲精品你懂的| 动漫av一区二区三区|