• source blog.cloudflare.com/how
  • 16 Jun 2015 by Marek Majkowski.
  • 該譯文及發布已得到原作者Marek Majkowski許可, 如需要請求許可來源請直接聯繫我.
  • 本文禁止轉載.

上週, 在一次偶然的談話中, 我無意中聽到一位同事說: "Linux網路棧很慢! 你不能期望它每秒每核心處理超過5萬個包!"

我想, 雖然我同意每核心50kpps可能是任何實際應用程序的限制, 但是Linux網路棧的極限是怎樣的? 讓我們重新表述一下, 讓它更有趣:

在Linux上, 編寫一個每秒接收100萬個UDP包的程序有多困難?

希望這篇文章是一個關於現代網路堆棧設計的不錯的經驗.

CC BY-SA 2.0 image by Bob McCaffrey

首先, 讓我們假設:

  • 測量每秒數據包(PPS)比測量每秒位元組(BPS)要有趣得多. 通過更好的pipline和發送更長的數據包, 可以實現高的BPS. 而改善PPS則要困難得多.
  • 由於我們對PPS感興趣, 我們的實驗將使用短UDP信息. 確切地說, 32位元組的UDP載荷. 這意味著 Ethernet Layer 上的74位元組.
  • 對於實驗, 我們將使用兩個物理伺服器: "receiver" 和 "sender".
  • 它們都有兩個6核心2GHz Xeon處理器. 使用超線程(HT)使每個伺服器上有24個處理器. 伺服器上裝有 Solarflare 的多隊列10G網卡(NIC), 並且配置了11個接收隊列. 詳情見稍後的內容.
  • 測試程序的源代碼可在這裡獲得: udpsender, udpreceiver

前提

我們使用4321埠用來收發UDP數據包. 在開始之前, 我們必須確保通信不會受到iptables的幹擾:

receiver$ iptables -I INPUT 1 -p udp --dport 4321 -j ACCEPT
receiver$ iptables -t raw -I PREROUTING 1 -p udp --dport 4321 -j NOTRACK

配置一些IP地址方便稍後使用:

receiver$ for i in `seq 1 20`; do
ip addr add 192.168.254.$i/24 dev eth2;
done
sender$ ip addr add 192.168.254.30/24 dev eth3

1. 最簡單的方法

首先讓我們做一個最簡單的實驗. 一個簡單的 sender 和 receiver 可以發送多少數據包?

sender偽代碼:

fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fd.bind(("0.0.0.0", 65400)) # select source port to reduce nondeterminism
fd.connect(("192.168.254.1", 4321))
while True:
fd.sendmmsg(["x00" * 32] * 1024)

雖然我們可以使用通常的 send syscall, 但它並不高效. 最好避免內核的上下文切換. 好在最近Linux中添加了一個可以一次發送多個數據包的 syscall: sendmmsg . 我們來一次發送1024個包.

receiver偽代碼:

fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fd.bind(("0.0.0.0", 4321))
while True:
packets = [None] * 1024
fd.recvmmsg(packets, MSG_WAITFORONE)

recvmmsg 是類似 recv syscall的更高效的版本.

讓我們試一下:

sender$ ./udpsender 192.168.254.1:4321
receiver$ ./udpreceiver1 0.0.0.0:4321
0.352M pps 10.730MiB / 90.010Mb
0.284M pps 8.655MiB / 72.603Mb
0.262M pps 7.991MiB / 67.033Mb
0.199M pps 6.081MiB / 51.013Mb
0.195M pps 5.956MiB / 49.966Mb
0.199M pps 6.060MiB / 50.836Mb
0.200M pps 6.097MiB / 51.147Mb
0.197M pps 6.021MiB / 50.509Mb

使用簡單的實現的情況下, 我們的數據可以達到197k-350kpps之間. 這個數據還可以. 不過pps的抖動相當大. 這是由於kernel把我們的程序在不同的CPU內核上不斷地切換造成的. 將進程與CPU核心錨定會避免這個問題:

sender$ taskset -c 1 ./udpsender 192.168.254.1:4321
receiver$ taskset -c 1 ./udpreceiver1 0.0.0.0:4321
0.362M pps 11.058MiB / 92.760Mb
0.374M pps 11.411MiB / 95.723Mb
0.369M pps 11.252MiB / 94.389Mb
0.370M pps 11.289MiB / 94.696Mb
0.365M pps 11.152MiB / 93.552Mb
0.360M pps 10.971MiB / 92.033Mb

現在, kernel scheduler將進程保持在定義好的CPU上, 提升處理器緩存的局部性(cache locality)訪問效果, 最終使pps數據更一致, 這正是我們想要的.

2. 發送更多的數據包

雖然 370k pps 對於一個簡單的程序來說還不錯, 但它離1Mpps的目標還很遠. 要接收更多的數據包, 首先我們必須發送更多的數據包. 下面我們來嘗試使用兩個獨立的線程來發送數據:

sender$ taskset -c 1,2 ./udpsender
192.168.254.1:4321 192.168.254.1:4321
receiver$ taskset -c 1 ./udpreceiver1 0.0.0.0:4321
0.349M pps 10.651MiB / 89.343Mb
0.354M pps 10.815MiB / 90.724Mb
0.354M pps 10.806MiB / 90.646Mb
0.354M pps 10.811MiB / 90.690Mb

接收方的收包數量沒有增加. ethtool -S 將揭示包的實際去向:

receiver$ watch sudo ethtool -S eth2 |grep rx
rx_nodesc_drop_cnt: 451.3k/s
rx-0.rx_packets: 8.0/s
rx-1.rx_packets: 0.0/s
rx-2.rx_packets: 0.0/s
rx-3.rx_packets: 0.5/s
rx-4.rx_packets: 355.2k/s
rx-5.rx_packets: 0.0/s
rx-6.rx_packets: 0.0/s
rx-7.rx_packets: 0.5/s
rx-8.rx_packets: 0.0/s
rx-9.rx_packets: 0.0/s
rx-10.rx_packets: 0.0/s

通過這些統計數據, NIC報告說, 它已經向 rx-4 隊列成功發送了大約350k pps. rx_nodesc_drop_cnt是一個 Solarflare 特有的計數器, 表示有 450kpps 的數據 NIC 未能向內核成功送達.

有時不清楚為什麼沒有送達數據包. 在我們的例子中, 很明顯: 隊列 4-rx 向 CPU #6(原文這裡是#4, 但是htop中滿載的CPU是#6, 故修改為#6) 發送數據包. 而 CPU #6 不能處理更多的包, 它讀取350kpps左右就滿負載了. 以下是htop中的情況:

多隊列NICs速成課程

過去網卡只有一個RX隊列用於在硬體和kernel之間傳遞數據包. 這種設計有一個明顯的侷限性, 交付的數據包數量不可能超過單個CPU的處理能力.

為了使用多核系統, NICs 開始支持多個 RX 隊列. 設計很簡單:每個RX隊列被錨定到一個單獨的CPU上, 因此, 只要將包發送到RX隊列, NIC就可以使用所有的CPU. 但它提出了一個問題: 給定一個包, NIC如何決定用哪個RX隊列推送數據包?

Round-robin balancing 是不可接受的, 因為它可能會在單個連接中引起包的重新排序問題. 另一種方法是使用包的哈希來決定RX隊列號. 哈希通常從一個元組(src IP, dst IP, src port, dst port)中計算. 這保證了單個連接的包總是會在完全相同的RX隊列上, 不會發生單個連接中的包的重新排序.

在我們的例子中, 哈希可以這樣使用:

RX_queue_number = hash(192.168.254.30, 192.168.254.1, 65400, 4321) % number_of_queues

多隊列散列演算法

哈希演算法可以通過 ethtool 配置. 我們的設置是:

receiver$ ethtool -n eth2 rx-flow-hash udp4
UDP over IPV4 flows use these fields for computing Hash flow key:
IP SA
IP DA

這相當於: 對於IPv4 UDP數據包, NIC將哈希(src IP, dst IP)地址. 例如:

RX_queue_number = hash(192.168.254.30, 192.168.254.1) % number_of_queues

因為忽略了埠號所以結果範圍非常有限. 許多NIC是允許定製hash演算法的. 同樣, 使用ethtool, 我們可以選擇用於哈希的元組(src IP, dst IP, src Port, dst Port):

receiver$ ethtool -N eth2 rx-flow-hash udp4 sdfn
Cannot change RX network flow hashing options: Operation not supported

不幸的是, 我們的NIC不支持. 所以我們的實驗被限制為對(src IP, dst IP)的哈希.

關於NUMA性能的說明

到目前為止, 我們所有的包只流到一個RX隊列中, 只訪問了一個CPU.

讓我們利用這個機會來測試不同CPU的性能. 在我們的設置中, receiver主機有兩個獨立的CPU插槽, 每個插槽都是不同的 NUMA node.

我們可以通過設置將 receiver 線程固定到 四個方案中的一個. 四種選擇是:

  • 在一個CPU上運行 receiver, 並且在相同的 NUMA 節點的另一個CPU運行RX隊列. 我們在上面看到的性能大約是360kpps.
  • receiver 使用與RX隊列完全相同的CPU, 我們可以得到 ~ 430kpps. 但它造成了極高的的抖動. 如果NIC被數據包淹沒, 性能就會降到零.
  • 當 receiver 運行在 CPU處理RX隊列的HT對等端上時, 其性能大約是平時的一半, 大約200kpps.
  • receiver 運行在與RX隊列不同的NUMA節點上, 我們得到了 ~ 330k pps. 但性能並不太穩定.

雖然在不同的NUMA節點上運行10%的性能損失聽起來不算太糟, 但隨著規模的擴大, 問題只會變得更糟. 在一些測試情況中, 只能榨出250kpps每core. 在所有的跨NUMA節點測試中, 抖動穩定性很差. 在更高的吞吐量下, NUMA節點之間的性能損失更加明顯. 在其中一個測試中, 當在一個糟糕的NUMA節點上運行 receiver 時, 到了4x性能損失的結果.

3. 多個接收IP地址

由於我們的NIC上的哈希演算法非常受限, 因此在多個RX隊列中分發數據包的唯一方法就是使用多個IP地址. 以下是如何發送數據包到不同目的地IP的例子:

sender$ taskset -c 1,2 ./udpsender 192.168.254.1:4321 192.168.254.2:4321

ethtool 確認數據包到達不同的RX隊列:

receiver$ watch sudo ethtool -S eth2 |grep rx
rx-0.rx_packets: 8.0/s
rx-1.rx_packets: 0.0/s
rx-2.rx_packets: 0.0/s
rx-3.rx_packets: 355.2k/s
rx-4.rx_packets: 0.5/s
rx-5.rx_packets: 297.0k/s
rx-6.rx_packets: 0.0/s
rx-7.rx_packets: 0.5/s
rx-8.rx_packets: 0.0/s
rx-9.rx_packets: 0.0/s
rx-10.rx_packets: 0.0/s

接收部分:

receiver$ taskset -c 1 ./udpreceiver1 0.0.0.0:4321
0.609M pps 18.599MiB / 156.019Mb
0.657M pps 20.039MiB / 168.102Mb
0.649M pps 19.803MiB / 166.120Mb

好快! 兩個核忙於處理RX隊列, 第三個核運行應用程序, 可以得到 ~ 650k pps!

我們可以通過向3個或4個RX隊列發送數據來進一步增加這個數字, 但是很快應用程序就會達到另一個限制. 這次 rx_nodesc_drop_cnt 沒有增長, 但 netstat 的"receive errors"卻是:

receiver$ watch netstat -s --udp
Udp:
437.0k/s packets received
0.0/s packets to unknown port received.
386.9k/s packet receive errors
0.0/s packets sent
RcvbufErrors: 123.8k/s
SndbufErrors: 0
InCsumErrors: 0

這意味著, 雖然NIC能夠將包傳遞給kernel, 但是kernel不能將包傳遞給應用程序. 在我們的例子中, 它只能送達440kpps, 剩餘的390kpps(packet receive errors) + 123kpps(RcvbufErrors)由於應用程序接收不夠快而被丟棄.

4. 多線程接收

我們需要擴展 receiver. 想要從多線程接收數據, 我們的簡單程序並不能很好地工作:

sender$ taskset -c 1,2 ./udpsender 192.168.254.1:4321 192.168.254.2:4321
receiver$ taskset -c 1,2 ./udpreceiver1 0.0.0.0:4321 2
0.495M pps 15.108MiB / 126.733Mb
0.480M pps 14.636MiB / 122.775Mb
0.461M pps 14.071MiB / 118.038Mb
0.486M pps 14.820MiB / 124.322Mb

與單線程程序相比, 接收性能反而會下降. 這是由UDP receive緩衝區上的鎖競爭引起的. 由於兩個線程都使用相同的套接字描述符(socket descriptor), 它們花費了很大比例的時間在圍繞UDP receive緩衝區進行鎖競爭. 這篇文章 對此問題進行了較為詳細的描述.

使用多個線程從單個描述符(descriptor)接收不是最佳選擇.

5. SO_REUSEPORT

幸運的是, 最近Linux中添加了一個變通的方法: SO_REUSEPORT flag . 當在套接字描述符(socket descriptor)上設置此標誌(flag)時, Linux將允許許多進程綁定到同一個埠上. 實際上, 任何數量的進程都可以綁定到它上面, 並且負載將分散到進程之間.

使用 SO_REUSEPORT, 每個進程將有一個單獨的套接字描述符(socket descriptor). 因此, 每個進程都將擁有一個專用的UDP接收緩衝區. 這就避免了之前遇到的競爭問題:

receiver$ taskset -c 1,2,3,4 ./udpreceiver1 0.0.0.0:4321 4 1
1.114M pps 34.007MiB / 285.271Mb
1.147M pps 34.990MiB / 293.518Mb
1.126M pps 34.374MiB / 288.354Mb

這纔像話!吞吐量現在還不錯!

我們的方案還有改進空間. 儘管我們啟動了四個接收線程, 但是負載並沒有均勻地分佈在它們之間:

兩個線程接收了所有的工作, 另外兩個線程根本沒有收到數據包. 這是由哈希衝突引起的, 但這次是在 SO_REUSEPORT 層.

結語

我還做了一些進一步的測試, 通過在單個NUMA節點上完全對齊的RX隊列和 receiver 線程, 可以獲得1.4Mpps. 在一個不同的NUMA節點上運行 receiver 會導致數字下降, 最多達到1Mpps.

總之, 如果想要完美的性能, 你需要:

  • 確保通信均勻地分散在多個RX隊列和 SO_REUSEPORT 進程中. 在實踐中, 只要有大量的連接(或流量), 負載通常是分佈良好的.
  • 從內核接收的數據包需要有足夠的空閑CPU來承載.
  • 為了更好的性能, RX隊列和 receiver 進程都應該位於單個NUMA節點上.

雖然我們已經展示了在Linux機器上接收1Mpps在技術上是可能的, 但是應用程序並沒有對接收到的數據包進行任何實際處理, 它甚至沒有查看流量的內容. 不要期望任何處理大量業務的實際應用程序都具有這樣的性能.

原文評論

  • willy ? 3 years ago
    • 我對你這麼低的數字感到非常驚訝, 我很想知道你是如何實現你的程序的, 因為我得到了明顯不同的結果, 即使是最簡單的send() / recv()方法在單個核心上運行, 沒有線程或其他任何東西:

sender$ taskset -c 1 ./snd -l 172.16.0.12:4000 -n 10000000 -m 32
10000000 packets sent in 9832132 us

receiver$ taskset -c 1 ./rcv -l 172.16.0.12:4000
1048576 packets in 1101686 us = 0.952 Mpps
1048576 packets in 1022431 us = 1.026 Mpps
1048576 packets in 1023755 us = 1.024 Mpps
1048576 packets in 1020582 us = 1.027 Mpps
1048576 packets in 1022920 us = 1.025 Mpps
1048576 packets in 1025329 us = 1.023 Mpps
1048576 packets in 1022739 us = 1.025 Mpps
1048576 packets in 1022317 us = 1.026 Mpps
1048576 packets in 1022663 us = 1.025 Mpps
^C

And as you can see with strace, its very naive :

04:48:37.599730 sendto(3, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 32, MSG_DONTWAIT|MSG_NOSIGNAL, {sa_family=AF_INET, sin_port=htons(4000), sin_addr=inet_addr("172.16.0.12")}, 16) = 32
04:48:37.599782 sendto(3, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 32, MSG_DONTWAIT|MSG_NOSIGNAL, {sa_family=AF_INET, sin_port=htons(4000), sin_addr=inet_addr("172.16.0.12")}, 16) = 32
04:48:37.599831 sendto(3, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 32, MSG_DONTWAIT|MSG_NOSIGNAL, {sa_family=AF_INET, sin_port=htons(4000), sin_addr=inet_addr("172.16.0.12")}, 16) = 32
^C

04:48:37.599754 recvfrom(3, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 32, MSG_NOSIGNAL, {sa_family=AF_INET, sin_port=htons(4824), sin_addr=inet_addr("172.16.0.11")}, [16]) = 32
04:48:37.599806 recvfrom(3, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 32, MSG_NOSIGNAL, {sa_family=AF_INET, sin_port=htons(4824), sin_addr=inet_addr("172.16.0.11")}, [16]) = 32
04:48:37.599856 recvfrom(3, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 32, MSG_NOSIGNAL, {sa_family=AF_INET, sin_port=htons(4824), sin_addr=inet_addr("172.16.0.11")}, [16]) = 32
^C

    • 通常, 當使用sendmmsg(), mmap和其他類似函數的時候, 可以實現每個內核 n Mpps, 而不僅僅是接近1Mpps. 我看到你配置了iptables, 所以它不可能是數據表現不佳的原因. 你確定沒有使用抽象套接字的lib或框架, 導致了不是按照你的需求(發送或接收數據包)而是在下面做大量的無用功嗎? 在我看來, 所有你需要做的調優和優化都是在解決這些東西帶來的問題. - Robert Engels to willy ? a year ago - 你在兩臺不同的機器上運行 sender 和 receiver 嗎? - Zerd to willy ? 3 years ago - 我試著用類似的方法復現你的測試結果, 但只達到了60kpps. 你介意分享你的代碼嗎? 你做了其他的參數調整嗎? - willy to Zerd ? 3 years ago - 當然, 代碼可以在這裡找到: 1wt.eu/tools/udp-test/ 我希望你意識到60kpps非常糟糕, 它說明你的設置中肯定有錯誤. 我曾經在1999年能夠使用奔騰mmx-233和Adaptec Starfire NIC(線速為100Mbps NICs)可以處理114kpps, 這個結果是你觀測到的結果的兩倍! 順便問一下, 你用的是什麼網卡? 你試過換另一種型號(和/或品牌)嗎? 即使是最便宜的realtek也能達到6-800kpps, 所以你的網卡(或它的驅動程序)可能完全壞了. - Xiao Ma to willy ? 2 years ago - 我沒有看到任何關於60kpps的結果, 結果數據在哪裡? - Zerd to willy ? 3 years ago - 謝謝你的分享. 我基於一個廉價的虛擬機得到了第一個測試結果. 在另一個更強的虛擬機(E5-2680 v3, VMware)上測試, 仍然只有200Mpps. 然後在真正的硬體上測試一個舊的AMD和板載NIC, 結果是600Mpps. 我可以預測在更好的硬體上可以達到100萬. 虛擬化在網路/中斷處理方面還有一段路要走. - eastdakota Mod to Zerd ? 3 years ago - 這就是5年前我們放棄在CloudFlare運行虛擬機的原因. :-) - willy to Zerd ? 3 years ago - 啊, 當然, 如果用虛擬機, 你浪費了你的時間:-) 虛擬機在涉及包處理的時候是相當恐怖的. 無論你這邊做什麼優化, 在驅動側只會提升1/100或1/1000的性能. 我很高興你得到了正確的測試結果, 即使這是一個痛苦的方法. - Xiao Ma to Zerd ? 2 years ago - 你是說200Mpps還是200Kpps? - Wine Twat to Xiao Ma ? 4 months ago - 200Kpps

  • Evil Bifrost ? 3 years ago
    • 接收1Mpps並不難, 我們甚至可以像Chelsio和FreeBSD那樣討論40Mpps chelsio.com/wp-content/ 即使英特爾DPDK性能更好, 但netmap也還是非常通用的. Solarflare Onload 是有效的, 但需要工作在UDP下.
  • Carlos Torres ? 3 years ago
    • 我想知道是不是NIC的IRQ親緣性問題導致了數據包在線程間的不均勻分佈. 查看 /procts/interrupts 應該可以得到保持均衡的方案.

  • Marcelo Dantas ? 3 years ago
    • 嗨Marek, 我想用你的代碼產生大量的數據包. 但必須改用syscall, 因為在我的Centos glibc上sendmmsg不可用. 我不關心接收端, 因為我想要測試一個線路中間設備, 它應該能停止"攻擊"(或者被打死). 現在我可以發送大約2.3Mpps, 但是我看到在發送方機器上這都發生在tx隊列#4上, 而我使用的是8個cpu中的4個來發送流量. 我感覺可以產生更多的流量, 但我是一個新手, 不知道如何(或者是否可以)在其他tx隊列上分配流量生成, 並達到更高的測試結果. 如有任何想法, 指導或例子, 我們將不勝感激. 因為我是新手, 那麼也許一步一步的教程(就像你的那樣, 讓我快速理解)是最好的. 提前感謝所有能幫忙的人. 馬塞洛.
  • Edwin ? 3 years ago
    • 在第5節: 當你有多個線程訂閱同一個組和埠時, 我覺得你所做的就是獲得相同流量的多個副本. 因此, 在每個傳入數據包僅由幾個核心之一處理一次的意義上, 並沒有真正的均衡負載. 重用埠意味著不止一個應用程序可以接收副本(例如, 常規用戶應用程序和流量嗅探應用程序). 這被認為是一個潛在的安全問題, 因此創建套接字的第一個線程必須明確允許後來的線程重用埠, 因此創建了該屬性. 另外, 在你的文章中, 你提到了solarflare和一些統計數據, 但它並沒有明顯加速了你的應用程序, 你的代碼也沒有明確使用它. Onload允許您使用單獨的隊列, 而不必擔心哈希問題.

  • Brian Bulkowski ? 3 years ago
    • 在Aerospike上, 我們通常在每臺伺服器上每秒處理100萬個資料庫事務, 並且沒有用批處理模式. 這意味著 1M 收包, 1M發包, TCP, 並且還有資料庫語義, 運行在在單個商業級(但是是高端)Linux節點上. 我們甚至在linux (amazon linux)的虛擬機中也這樣做了. 在實驗室環境中, 我們甚至在從存儲子系統(Intel P3700 Flash)讀取100萬次的同時完成了上面的測試. 多隊列NIC是必需的, 更高的核心速度是更好的, 但是根據我們的基準測試, TCP比UDP更快(代碼路徑問題, RPS更好的分配到核心). 如果您想了解我們是如何做到這一點的, 請查看源代碼, 這是最好的資源.
  • Jens Timmerman ? 3 years ago
    • 等等, 您需要讓 sender 和 receiver 位於同一個NUMA節點上? 那還為什麼要使用網路呢? 可以直接用命名管道或unix套接字進行通信...
    • Navin Shajan to Jens Timmerman ? 3 years ago
      • 他搞砸了: 測試是在vm上進行的, 而不是真正的機器.

  • Raghavendra Prabhu ? 3 years ago
    • 您是否嘗試使用 / test與RFS(或ARFS): access.redhat.com/docum. 這可能會減少固定所需的工作, 並提供更好的緩存利用率.
  • Bram Server ? 3 years ago
    • PF_RING 怎麼樣? ntop.org/products/packe ixgbe: 11Mpps

  • Sareena Kp ? 2 years ago
    • 謝謝. 這個博客對我們在實驗室裏打開10G卡非常有用. 我對linux網路沒有什麼經驗. 我對博客中顯示的netstat和ethtool的輸出有疑問. 你如何能得到/s率在netstat和ethtool的輸出

receiver$ watch netstat -s --udp
Udp:
437.0k/s packets received
0.0/s packets to unknown port received.
386.9k/s packet receive errors
0.0/s packets sent

    • 我一直在尋找工具. 所有的工具只顯示數字而不是速率. 每秒的速率非常有用. 請問您是否對工具做了修改, 並更改了其他設置? 謝謝, Sareena.

  • Usman ? 2 years ago
    • 我們有一個系統, 我們想要處理DDOS攻擊, 我們微調它的方式是, 一個核心將包從rx隊列分發到不同的cpu. 通過這種方式, 我們還可以通過為內部通信分配一個隊列來保護集羣通信不受DDOS通信的影響.
  • V_CM ? 3 years ago

    • 1.4Mpps遠不是Linux堆棧能夠實際交付的. 這是一個提綱(需要谷歌翻譯), 一個傢伙描述瞭如何製作一個應用程序, 可以捕獲9 Mpps. habr.com/post/261161/

  • Ben Hutchings ? 3 years ago
    • "不幸的是, 我們的NIC不支持它, 我們受限於(src IP, dst IP)哈希. " 我認為SFC9100系列可能會支持在RSS哈希中包含UDP埠, 但是當前的固件可能不支持它. 對於SFC9000家族, 可以選擇使用"Falcon"散列, 其中包括UDP埠, 而不是MS Toeplitz散列. 我沒有在驅動程序中啟用它, 因為在使用Falcon散列時, 有一個硬體bug破壞了散列插入. 另外, 我不確定Falcon散列如何與間接表交互.
  • Dan Palmer ? 3 years ago
    • 不是吹毛求疵, 這是一篇很吸引人的文章, 但重點似乎是要證明你的同事關於"Linux上的50kpps / core"的看法是錯誤的, 因為這很緩慢, 然而這似乎正是你所實現的? 50,000 pps * 24核= 1,200,000 pps 這似乎是大致的成果, 因此證明最初的數字是正確的?我不會認為這個數字"慢", 但這個數字是正確的.
    • Lucas Rovaris to Dan Palmer ? 3 years ago
      • 不, 再讀一遍, 他用4核而不是24核實現了12mpps, 以此證明瞭他的觀點.

  • Eugene Beresovsky ? 3 years ago
    • 感謝Marek分享這些結果. 我有幾個問題. 1)Linux網路棧很慢 相對於什麼? BSD? Windows(版本)? Kernel bypass? 我有點驚訝, 因為所有從事超低延遲, 高吞吐量(Mpps, 亞微秒延遲)聯網業務的人似乎都在使用Linux, 而不是直接使用FPGA. 在一些情況下, 它甚至被明確地解釋為"在標準的Linux內核之上", 比如Arista 7124SX交換機arista.com/assets/data/, 使用一個雙核x86 CPU作為控制平面. 對於每個埠(24個埠提供的總速度約為每秒3.6億), 該交換機可以完成最大14.88Mpps. 當然, 這是一種特殊的, 單一用途的應用程序, 但Argon Design公司在2013年使用了這種交換機的一個變種, 以使"達到延遲降低到35納秒"argondesign.com/media/u. Argon公司表示:"目前用於交易的絕大多數現有計算代碼都是為x86處理器架構的Linux編寫的. "我在superuser網站上的問題是:"是否有可能每秒處理數百萬個數據報?" superuser.com/questions 我不知道他們中有多少人在使用標準網路堆棧, 這提醒了我, 正如其他人已經暗示的:當您使用SolarFlare NICs時, 您是否也將這些數字與onload進行了比較? 2)您的"Rx隊列#4向CPU #4發送數據包"語句與htop截圖不匹配, htop截圖顯示了一個掛起的CPU #5 (htop的nubmered"6") 只是一個打字錯誤還是一個不同的測試運行?另外, 如果你能在分配完負載後添加一個htop屏幕截圖, 那就太好了. 3)你考慮過netmap嗎 不是標準的linux網路堆棧, 但是由於您對高pps率感興趣, 您可能需要考慮它. 請注意, 在他們的原型中, "一個運行在900MHz的單核能夠發送或接收14.88Mpps" info.iet.unipi.it/~luig 這是為64位元組幀(包括乙太網CRC). 它不使用套接字API, 但是除了它的本機API之外, 還提供了pcap API. 4) 64位元組幀的pps率:14.88Mpps. 65位元組幀的速率:大約一半(大約相當於128位元組幀) 這在netmap的手冊中非常顯眼, 類似的事情可能會影響你的74位元組(或78位元組,包括乙太網CRC)測試, 如果不是你的接收速率接近理論最大值, 而且還遠離 7或8 mmp 並且限大小> = 65位元組. 但也許還是值得一試.
      • epiphany to Eugene Beresovsky ? 3 years ago
        • 順便說一下, Arista公司的雙核CPU運行的控制平面與傳輸幀無關, 所以你關於CPU性能的說法是非常誤導的.
  • Rüdiger M?ller ? 3 years ago
    • 在120萬次運行時, drop/error 率是多少?

  • zoobab ? 3 years ago
    • 您是否嘗試過僅僅通過安裝netsniff-ng就可以計算出多少個PPS ?它使用了一些PF_RING內核函數, 它跳過了linux tcpip棧的大部分, 並允許零拷貝.
  • Shane Duffy ? 3 years ago
    • 那麼來自Github的Brubeck呢?全新設計的StatsD風格 collector/Metrics 引擎. 我想其中一個因素是, 他們從不同的客戶那裡得到服務. githubengineering.com/b 每秒超過430萬個metrics. 包是UDP github.com/github/brube

推薦閱讀:

查看原文 >>
相關文章