linux性能優化-網絡

網絡性能指標
  • 帶寬:鏈路最大傳輸速率 b/s(比特每秒)
  • 吞吐量:單位時間成功傳輸的數據量,單位b/s
  • 延時:建立連接需要的時間或一個數據包往返時間RTT
  • PPS:以網絡包為單位的傳輸速率,PPS用來評估網絡的轉發能力
  • 網絡可用性 併發連接數 丟包率 重傳率也是常用性能指標
網路配置

可以使用ifconfig或ip查看網絡的配置

套接字信息

ifconfig和ip只顯示了網絡接口收發數據包的統計信息,網絡協議棧中的統計信息,我們也必須關注。你可以用 netstat或者ss,來查看套接字、網絡棧、網絡接口以及路由表的信息。

可以使用ifconfig、netstat、ss、sar、ping 等工具查看網絡性能。

C10K問題

99年提出,如何在32位 2GB內存 千兆網卡支持每秒1萬併發。資源上來講,只要每個請求處理佔用不到200KB內存和100Kbit網絡帶寬即可,物理資源足夠,然後就是軟件問題,特別是網絡I/O模型問題。

C10K之前,linux中網絡處理都用同步阻塞的方式,即每個請求都分配一個進程或者線程,但是有10000個進程或者線程的調度、上下文切換乃至佔用的內存都會成為瓶頸。為了支持1w併發,需要解決:

  1. 怎樣在一個線程內處理多個請求,即一個線程內響應多個網絡I/O;
  2. 更節省資源的處理客戶請求,是否可以使用更少的線程

select,poll,epoll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的

I/O模型優化
  • 水平觸發:只要文件描述符可以非阻塞地執行 I/O ,就會觸發通知。也就是說,應用程序可以隨時檢查文件描述符的狀態,然後再根據狀態,進行 I/O 操作
  • 邊緣觸發:只有在文件描述符的狀態發生改變(也就是 I/O 請求達到)時,才發送一次 通知。這時候,應用程序需要儘可能多地執行 I/O,直到無法繼續讀寫,才可以停止。 如果 I/O 沒執行完,或者因為某種原因沒來得及處理,那麼這次通知也就丟失了
  1. 使用非阻塞I/O和水平觸發(select或poll)。select 和poll需要從文件描述符列表中,找出哪些可以執行 I/O ,然後進行真正的網絡 I/O 讀寫。由於 I/O 是非阻塞的,一個線程中就可以同時監控一批套接字的文件描述符,這樣就達到了單線程處理多請求的目的。 所以,這種方式的最大優點,是對應用程序比較友好,它的 API 非常簡單。 但是,應用軟件使用 select和poll時,需要對這些文件描述符列表進行輪詢,這樣請求數多的時候就會比較耗時。並且,select 和poll 還有一些其他的限制。select使用固定長度的位相量,表示文件描述符的集合,因此會有最大描述符數量的限 制。比如,在 32 位系統中,默認限制是 1024。並且,在 select 內部,檢查套接字狀態 是用輪詢的方法,再加上應用軟件使用時的輪詢,就變成了一個 O(n^2) 的關係

poll 改進了 select 的表示方法,換成了一個沒有固定長度的數組,這樣就沒有了最大 描述符數量的限制(當然還會受到系統文件描述符限制)。但應用程序在使用 poll 時,同 樣需要對文件描述符列表進行輪詢,這樣,處理耗時跟描述符數量就是 O(N) 的關係;除此之外,應用程序每次調用 select 和poll時,還需要把文件描述符的集合,從用戶空 間傳入內核空間,由內核修改後,再傳出到用戶空間中。這一來一回的內核空間與用戶空 間切換,也增加了處理成本

  1. 非阻塞I/O和邊緣觸發通知(epoll)
  • epoll 使用紅黑樹,在內核中管理文件描述符的集合,這樣,就不需要應用程序在每次 操作時都傳入、傳出這個集合
  • epoll 使用事件驅動的機制,只關注有 I/O 事件發生的文件描述符,不需要輪詢掃描整 個集合。
  1. 異步I/O glibc 的 aio 有 bug , kernel的aio 只能以 O_DIRECT方式做直接 IO , libeio也是beta階段。epoll是成熟的,但是epoll本身是同步的。Linux上目前沒有像 IOCP這樣的成熟異步IO實現。
工作模型優化
  1. 主進程+多個worker子進程

主進程bind()+listen(),創建多個子進程;每個子進程,都通過accept()或epoll_wait()處理相同的套接字。 驚群問題:當網絡 I/O 事件發生時,多個進程被同時喚醒,但實際上只有一個進程來響應這個事件,其他被 喚醒的進程都會重新休眠。為了避免驚群問題, Nginx 在每個 worker 進程中,都增加一個了全局鎖 (accept_mutex)。這些 worker 進程需要首先競爭到鎖,只有競爭到鎖的進程,才會加 入到 epoll 中,這樣就確保只有一個 worker 子進程被喚醒。

  1. 監聽到相同端口的多進程模型 所有的進程都監聽相同的接口, 並且開啟 SO_REUSEPORT 選項,由內核負責將請求負載均衡到這些監聽進程中去
C1000K問題
  • 物力資源:每個請求16KB,約15GB內存;萬兆網卡
  • 軟件資源:大量的連接也會佔用大量的軟件資源,比如文件描述符的數 量、連接狀態的跟蹤(CONNTRACK)、網絡協議棧的緩存大小(比如套接字讀寫緩存、 TCP 讀寫緩存)等
  • 大量請求帶來的中斷處理,也會帶來非常高的處理成本。這樣,就需要多隊列網 卡、中斷負載均衡、CPU 綁定、RPS/RFS(軟中斷負載均衡到多個 CPU 核上),以及將 網絡包的處理卸載(Offload)到網絡設備(如 TSO/GSO、LRO/GRO、VXLAN OFFLOAD)等各種硬件和軟件的優化
  • C1000K 的解決方法,本質上還是構建在 epoll 的非阻塞 I/O 模型上。只不過,除了 I/O 模型之外,還需要從應用程序到 Linux 內核、再到 CPU、內存和網絡等各個層次的深度優 化,特別是需要藉助硬件,來卸載那些原來通過軟件處理的大量功能
C10M

C1000K 問題中,各種軟件、硬件的優化很可能都已經做到頭了。特別是當升 級完硬件(比如足夠多的內存、帶寬足夠大的網卡、更多的網絡功能卸載等)後,還是比較難實現的。究其根本,還是 Linux 內核協議棧做了太多太繁重的工作。從網卡中斷帶來的硬中斷處理程序開始,到軟中斷中的各層網絡協議處理,最後再到應用程序,這個路徑實在是太長了,就會導致網絡包的處理優化,到了一定程度後,就無法更進一步了。

要解決這個問題,最重要就是跳過內核協議棧的冗長路徑,把網絡包直接送到要處理的應用程序那裡去。這裡有兩種常見的機制,DPDK 和 XDP

  • 第一種機制,DPDK,是用戶態網絡的標準。它跳過內核協議棧,直接由用戶態進程通過輪詢的方式,來處理網絡接收。在 PPS 非常高的場景中,查詢時間比實際工作時間少了很多,絕大部分時間都在處理網 絡包; 而跳過內核協議棧後,就省去了繁雜的硬中斷、軟中斷再到 Linux網絡協議棧逐層處理 的過程,應用程序可以針對應用的實際場景,有針對性地優化網絡包的處理邏輯,而不 需要關注所有的細節。此外,DPDK還通過大頁、CPU綁定、內存對齊、流水線併發等多種機制,優化網絡包的 處理效率。
  • XDP(eXpress Data Path),則是 Linux 內核提供的一種高性能網絡數據 路徑。它允許網絡包,在進入內核協議棧之前,就進行處理,也可以帶來更高的性能。 XDP 底層跟我們之前用到的 bcc-tools 一樣,都是基於 Linux 內核的 eBPF 機制實現的

XDP 對內核的要求比較高,需要的是 Linux 4.8 以上版本,並且它也不提供 緩存隊列。基於 XDP 的應用程序通常是專用的網絡應用,常見的有 IDS(入侵檢測系 統)、DDoS 防禦、 cilium 容器網絡插件等

要實現 C10M,就不是增加物理資源、調優內核和應用程序可以解決的問題 了。這時內核中冗長的網絡協議棧就成了最大的負擔。

  • 需要用 XDP 方式,在內核協議棧之前,先處理網絡包。
  • 或基於 DPDK,直接跳過網絡協議棧,在用戶空間通過輪詢的方式處理。 其中,DPDK是目前最主流的高性能網絡方案,不過,這需要能支持DPDK的網卡配合使用.

基準測試

  • 在應用層,你可以使用 wrk、Jmeter 等模擬用戶的負載,測試應用程序的每秒請求數、 處理延遲、錯誤數等;
  • 在傳輸層,則可以使用 iperf 等工具,測試 TCP 的吞吐情況;
  • Linux 內核自帶的 pktgen ,測試服務器的 PPS。


分享到:


相關文章: