1. 程式人生 > 其它 >Linux網路優化

Linux網路優化

=======================Linux網路優化篇====================


概念:
網路七層模型:


應用層,負責為應用程式提供統一的介面。

表示層,負責把資料轉換成相容接收系統的格式。

會話層,負責維護計算機之間的通訊連線。

傳輸層,負責為資料加上傳輸表頭,形成資料包。

網路層,負責資料的路由和轉發。

資料鏈路層,負責 MAC 定址、錯誤偵測和改錯。

物理層,負責在物理網路中傳輸資料幀

網路四層模型:


應用層,負責向用戶提供一組應用程式,比如 HTTP、FTP、DNS 等。

傳輸層,負責端到端的通訊,比如 TCP、UDP 等。

網路層,負責網路包的封裝、定址和路由,比如 IP、ICMP 等。

網路介面層,負責網路包在物理網路中的傳輸,比如 MAC 定址、錯誤偵測以及通過網絡卡傳輸網路幀等。


網路介面配置的最大傳輸單元(MTU),就規定了最大的 IP 包大小。在我們最常用的乙太網中,MTU 預設值是 1500(這也是 Linux 的預設值)。一旦網路包超過 MTU 的大小,就會在網路層分片,以保證分片後的 IP 包不大於 MTU 值。顯然,MTU 越大,需要的分包也就越少,自然,網路吞吐能力就越好。

===============================================================================================================================================


Linux網路收發流程:


***接受過程


當一個網路幀到達網絡卡後,網絡卡會通過 DMA 方式,把這個網路包放到收包佇列中;然後通過硬中斷,告訴中斷處理程式已經收到了網路包。

接著,網絡卡中斷處理程式會為網路幀分配核心資料結構(sk_buff),並將其拷貝到 sk_buff 緩衝區中;然後再通過軟中斷,通知核心收到了新的網路幀。

接下來,核心協議棧從緩衝區中取出網路幀,並通過網路協議棧,從下到上逐層處理這個網路幀。比如,

在鏈路層檢查報文的合法性,找出上層協議的型別(比如 IPv4 還是 IPv6),再去掉幀頭、幀尾,然後交給網路層。

網路層取出 IP 頭,判斷網路包下一步的走向,比如是交給上層處理還是轉發。當網路層確認這個包是要傳送到本機後,就會取出上層協議的型別(比如 TCP 還是 UDP),去掉 IP 頭,再交給傳輸層處理。

傳輸層取出 TCP 頭或者 UDP 頭後,根據 < 源 IP、源埠、目的 IP、目的埠 > 四元組作為標識,找出對應的 Socket,並把資料拷貝到 Socket 的接收快取中。

最後,應用程式就可以使用 Socket 介面,讀取到新接收到的資料了。

***傳送過程


首先,應用程式呼叫 Socket API(比如 sendmsg)傳送網路包。

由於這是一個系統呼叫,所以會陷入到核心態的套接字層中。套接字層會把資料包放到 Socket 傳送緩衝區中。

接下來,網路協議棧從 Socket 傳送緩衝區中,取出資料包;再按照 TCP/IP 棧,從上到下逐層處理。比如,傳輸層和網路層,分別為其增加 TCP 頭和 IP 頭,執行路由查詢確認下一跳的 IP,並按照 MTU 大小進行分片。

分片後的網路包,再送到網路介面層,進行實體地址定址,以找到下一跳的 MAC 地址。然後新增幀頭和幀尾,放到發包佇列中。這一切完成後,會有軟中斷通知驅動程式:發包佇列中有新的網路幀需要傳送。

最後,驅動程式通過 DMA ,從發包佇列中讀出網路幀,並通過物理網絡卡把它傳送出去。

===============================================================================================================================================


效能指標:
頻寬,表示鏈路的最大傳輸速率,單位通常為 b/s (位元 / 秒)。

吞吐量,表示單位時間內成功傳輸的資料量,單位通常為 b/s(位元 / 秒)或者 B/s(位元組 / 秒)。吞吐量受頻寬限制,而吞吐量 / 頻寬,也就是該網路的使用率。

延時,表示從網路請求發出後,一直到收到遠端響應,所需要的時間延遲。在不同場景中,這一指標可能會有不同含義。比如,它可以表示,建立連線需要的時間(比如 TCP 握手延時),或一個數據包往返所需的時間(比如 RTT)。

PPS,是 Packet Per Second(包 / 秒)的縮寫,表示以網路包為單位的傳輸速率。PPS 通常用來評估網路的轉發能力,比如硬體交換機,通常可以達到線性轉發(即 PPS 可以達到或者接近理論最大值)。而基於 Linux 伺服器的轉發,則容易受網路包大小的影響。

除了這些指標,網路的可用性(網路能否正常通訊)、併發連線數(TCP 連線數量)、丟包率(丟包百分比)、重傳率(重新傳輸的網路包比例)等也是常用的效能指標。

===============================================================================================================================================


小記:ifconfig 和 ip 分別屬於軟體包 net-tools 和 iproute2,iproute2 是 net-tools 的下一代。通常情況下它們會在發行版中預設安裝。但如果你找不到 ifconfig 或者 ip 命令,可以安裝這兩個軟體包。

ifconfig 和 ip 命令輸出的指標基本相同


相關重要指標:


第一,網路介面的狀態標誌。ifconfig 輸出中的 RUNNING ,或 ip 輸出中的 LOWER_UP ,都表示物理網路是連通的,即網絡卡已經連線到了交換機或者路由器中。如果你看不到它們,通常表示網線被拔掉了。

第二,MTU 的大小。MTU 預設大小是 1500,根據網路架構的不同(比如是否使用了 VXLAN 等疊加網路),你可能需要調大或者調小 MTU 的數值。

第三,網路介面的 IP 地址、子網以及 MAC 地址。這些都是保障網路功能正常工作所必需的,你需要確保配置正確。

第四,網路收發的位元組數、包數、錯誤數以及丟包情況,特別是 TX 和 RX 部分的 errors、dropped、overruns、carrier 以及 collisions 等指標不為 0 時,通常表示出現了網路 I/O 問題。其中:

errors 表示發生錯誤的資料包數,比如校驗錯誤、幀同步錯誤等;

dropped 表示丟棄的資料包數,即資料包已經收到了 Ring Buffer,但因為記憶體不足等原因丟包

overruns 表示超限資料包數,即網路 I/O 速度過快,導致 Ring Buffer 中的資料包來不及處理(佇列滿)而導致的丟包;

carrier 表示發生 carrirer 錯誤的資料包數,比如雙工模式不匹配、物理電纜出現問題等;

collisions 表示碰撞資料包數。

===============================================================================================================================================
netstat ss命令各欄位說明:


當套接字處於連線狀態(Established)時,

Recv-Q 表示套接字緩衝還沒有被應用程式取走的位元組數(即接收佇列長度)。

而 Send-Q 表示還沒有被遠端主機確認的位元組數(即傳送佇列長度)。

當套接字處於監聽狀態(Listening)時,

Recv-Q 表示 syn backlog 的當前值。

而 Send-Q 表示最大的 syn backlog 值。

半連線:

就是還沒有完成 TCP 三次握手的連線,連線只進行了一半,而伺服器收到了客戶端的 SYN 包後,就會把這個連線放到半連線佇列中,然後再向客戶端傳送 SYN+ACK 包。

全連線:

則是指伺服器收到了客戶端的 ACK,完成了 TCP 三次握手,然後就會把這個連線挪到全連線佇列中。這些全連線中的套接字,還需要再被 accept() 系統呼叫取走,這樣,伺服器就可以開始真正處理客戶端的請求了。

netstat -s/ss -s #檢視協議棧資訊

===============================================================================================================================================
網路吞吐和pps
sar 增加-n引數就可以檢視網路的統計資訊,比如網路介面(DEV)、網路介面錯誤(EDEV)、TCP、UDP、ICMP等
sar -n DEV 1 #獲取網路介面統計資訊,每秒輸出一組資料

sar -u #統計CPU的使用情況,每間隔1秒鐘統計一次總共統計三次:

#sar -u 1 3

sar -p 1 3#報個每個CPU的使用狀態:

#CPU 所有CPU的統計

#%user 使用者態的CPU使用統計

#%nice 更改過優先順序的程序的CPU使用統計

#%iowait CPU等待IO資料的百分比

#%steal 虛擬機器的vCPU佔用的物理CPU的百分比

#%idle 空閒的CPU百分比

sar -q #檢視平均負載:

#runq-sz 執行佇列的長度(等待執行的程序數,每核的CP不能超過3個)

#plist-sz 程序列表中的程序(processes)和執行緒數(threads)的數量

#ldavg-1 最後1分鐘的CPU平均負載,即將多核CPU過去一分鐘的負載相加再除以核心數得出的平均值,5分鐘和15分鐘以此類推

#ldavg-5 最後5分鐘的CPU平均負載

#ldavg-15 最後15分鐘的CPU平均負載

sar -r #檢視記憶體使用情況

#kbmemfree 空閒的實體記憶體大小

#kbmemused 使用中的實體記憶體大小

#%memused 實體記憶體使用率

#kbbuffers 核心中作為緩衝區使用的實體記憶體大小,kbbuffers和kbcached:這兩個值就是free命令中的buffer和cache.

#kbcached 快取的檔案大小

#kbcommit 保證當前系統正常執行所需要的最小記憶體,即為了確保記憶體不溢位而需要的最少記憶體(實體記憶體+Swap分割槽)

#commit 這個值是kbcommit與記憶體總量(實體記憶體+swap分割槽)的一個百分比的值

sar -W #檢視系統swap分割槽的統計資訊:

#pswpin/s 每秒從交換分割槽到系統的交換頁面(swap page)數量

#pswpott/s 每秒從系統交換到swap的交換頁面(swap page)的數量

sar -b #檢視I/O和傳遞速率的統計資訊

#tps 磁碟每秒鐘的IO總數,等於iostat中的tps

#rtps 每秒鐘從磁碟讀取的IO總數

#wtps 每秒鐘從寫入到磁碟的IO總數

#bread/s 每秒鐘從磁碟讀取的塊總數

#bwrtn/s 每秒鐘此寫入到磁碟的塊總數

sar -d #磁碟使用詳情統計

#DEV 磁碟裝置的名稱,如果不加-p,會顯示dev253-0類似的裝置名稱,因此加上-p顯示的名稱更直接

#tps:每秒I/O的傳輸總數

#rd_sec/s 每秒讀取的扇區的總數

#wr_sec/s 每秒寫入的扇區的 總數

#avgrq-sz 平均每次次磁碟I/O操作的資料大小(扇區)

#avgqu-sz 磁碟請求佇列的平均長度

#await 從請求磁碟操作到系統完成處理,每次請求的平均消耗時間,包括請求佇列等待時間,單位是毫秒(1秒等於1000毫秒),等於尋道時間+佇列時間+服務時間

#svctm I/O的服務處理時間,即不包括請求佇列中的時間

#%util I/O請求佔用的CPU百分比,值越高,說明I/O越慢

sar -v #程序、inode、檔案和鎖表狀態

#dentunusd 在緩衝目錄條目中沒有使用的條目數量

#file-nr 被系統使用的檔案控制代碼數量

#inode-nr 已經使用的索引數量

#pty-nr 使用的pty數量


各欄位含義:
rxpck/s 和 txpck/s 分別是接收和傳送的 PPS,單位為包 / 秒。

rxkB/s 和 txkB/s 分別是接收和傳送的吞吐量,單位是 KB/ 秒。

rxcmp/s 和 txcmp/s 分別是接收和傳送的壓縮資料包數,單位是包 / 秒。

%ifutil 是網路介面的使用率,即半雙工模式下為 (rxkB/s+txkB/s)/Bandwidth,而全雙工模式下為 max(rxkB/s, txkB/s)/Bandwidth。


ethtool eth0 | grep Speed #檢視網絡卡型別(千兆、萬兆)

===============================================================================================================================================
連通性和延時
ping -c3 ip地址 #測試連通性,傳送3次icmp包後停止

第一部分,是每個 ICMP 請求的資訊,包括 ICMP 序列號(icmp_seq)、TTL(生存時間,或者跳數)以及往返延時。

第二部分,則是三次 ICMP 請求的彙總。
===============================================================================================================================================


io模型優化---io的多路複用


io事件通知方式:
水平觸發:只要檔案描述符可以非阻塞地執行 I/O ,就會觸發通知。也就是說,應用程式可以隨時檢查檔案描述符的狀態,然後再根據狀態,進行 I/O 操作。

邊緣觸發:只有在檔案描述符的狀態發生改變(也就是 I/O 請求達到)時,才傳送一次通知。這時候,應用程式需要儘可能多地執行 I/O,直到無法繼續讀寫,才可以停止。如果 I/O 沒執行完,或者因為某種原因沒來得及處理,那麼這次通知也就丟失了。

三種io多路複用的實現方式:.


*********第一種,使用非阻塞 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 時,還需要把檔案描述符的集合,從使用者空間傳入核心空間,由核心修改後,再傳出到使用者空間中。這一來一回的核心空間與使用者空間切換,也增加了處理成本。

有沒有什麼更好的方式來處理呢?答案自然是肯定的。

********第二種,使用非阻塞 I/O 和邊緣觸發通知,比如 epoll。

既然 select 和 poll 有那麼多的問題,就需要繼續對其進行優化,而 epoll 就很好地解決了這些問題。

epoll 使用紅黑樹,在核心中管理檔案描述符的集合,這樣,就不需要應用程式在每次操作時都傳入、傳出這個集合。

epoll 使用事件驅動的機制,只關注有 I/O 事件發生的檔案描述符,不需要輪詢掃描整個集合。

不過要注意,epoll 是在 Linux 2.6 中才新增的功能(2.4 雖然也有,但功能不完善)。由於邊緣觸發只在檔案描述符可讀或可寫事件發生時才通知,那麼應用程式就需要儘可能多地執行 I/O,並要處理更多的異常事件。

********第三種,使用非同步 I/O(Asynchronous I/O,簡稱為 AIO)。在前面檔案系統原理的內容中,我曾介紹過非同步 I/O 與同步 I/O 的區別。非同步 I/O 允許應用程式同時發起很多 I/O 操作,而不用等待這些操作完成。而在 I/O 完成後,系統會用事件通知(比如訊號或者回調函式)的方式,告訴應用程式。這時,應用程式才會去查詢 I/O 操作的結果。

非同步 I/O 也是到了 Linux 2.6 才支援的功能,並且在很長時間裡都處於不完善的狀態,比如 glibc 提供的非同步 I/O 庫,就一直被社群詬病。同時,由於非同步 I/O 跟我們的直觀邏輯不太一樣,想要使用的話,一定要小心設計,其使用難度比較高。

===============================================================================================================================================
程序的工作模型:
第一種,主程序 + 多個 worker 子程序,這也是最常用的一種模型。這種方法的一個通用工作模式就是:

主程序執行 bind() + listen() 後,建立多個子程序;

然後,在每個子程序中,都通過 accept() 或 epoll_wait() ,來處理相同的套接字。

注:此種方式會存在驚群問題,此後通過全域性鎖解決,誰得到鎖誰就去處理

第二種,監聽到相同埠的多程序模型。在這種方式下,所有的程序都監聽相同的介面,並且開啟 SO_REUSEPORT 選項,由核心負責將請求負載均衡到這些監聽程序中去。
由於核心確保了只有一個程序被喚醒,就不會出現驚群問題了

===============================================================================================================================================
c1000k:
100萬併發請求的處理思路
假設每個請求需要 16KB 記憶體的話,那麼總共就需要大約 15 GB 記憶體。

而從頻寬上來說,假設只有 20% 活躍連線,即使每個連線只需要 1KB/s 的吞吐量,總共也需要 1.6 Gb/s 的吞吐量。千兆網絡卡顯然滿足不了這麼大的吞吐量,所以還需要配置萬兆網絡卡,或者基於多網絡卡 Bonding 承載更大的吞吐量。

其次,從軟體資源上來說,大量的連線也會佔用大量的軟體資源,比如檔案描述符的數量、連線狀態的跟蹤(CONNTRACK)、網路協議棧的快取大小(比如套接字讀寫快取、TCP 讀寫快取)等等。

最後,大量請求帶來的中斷處理,也會帶來非常高的處理成本。這樣,就需要多佇列網絡卡、中斷負載均衡、CPU 繫結、RPS/RFS(軟中斷負載均衡到多個 CPU 核上),以及將網路包的處理解除安裝(Offload)到網路裝置(如 TSO/GSO、LRO/GRO、VXLAN OFFLOAD)等各種硬體和軟體的優化。

C1000K 的解決方法,本質上還是構建在 epoll 的非阻塞 I/O 模型上。只不過,除了 I/O 模型之外,還需要從應用程式到 Linux 核心、再到 CPU、記憶體和網路等各個層次的深度優化,特別是需要藉助硬體,來解除安裝那些原來通過軟體處理的大量功能。

===============================================================================================================================================
c10m
1000萬併發請求處理思路
實際上,在 C1000K 問題中,各種軟體、硬體的優化很可能都已經做到頭了。特別是當升級完硬體(比如足夠多的記憶體、頻寬足夠大的網絡卡、更多的網路功能解除安裝等)後,你可能會發現,無論你怎麼優化應用程式和核心中的各種網路引數,想實現 1000 萬請求的併發,都是極其困難的。

究其根本,還是 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 容器網路外掛等。

===============================================================================================================================================
總結:
C10K 問題的根源,一方面在於系統有限的資源;另一方面,也是更重要的因素,是同步阻塞的 I/O 模型以及輪詢的套接字介面,限制了網路事件的處理效率。Linux 2.6 中引入的 epoll ,完美解決了 C10K 的問題,現在的高效能網路方案都基於 epoll。

從 C10K 到 C100K ,可能只需要增加系統的物理資源就可以滿足;但從 C100K 到 C1000K ,就不僅僅是增加物理資源就能解決的問題了。這時,就需要多方面的優化工作了,從硬體的中斷處理和網路功能解除安裝、到網路協議棧的檔案描述符數量、連線狀態跟蹤、快取佇列等核心的優化,再到應用程式的工作模型優化,都是考慮的重點。

再進一步,要實現 C10M ,就不只是增加物理資源,或者優化核心和應用程式可以解決的問題了。這時候,就需要用 XDP 的方式,在核心協議棧之前處理網路包;或者用 DPDK 直接跳過網路協議棧,在使用者空間通過輪詢的方式直接處理網路包。
========================================================================================
各協議層的效能指標:


1.轉發效能:


hping3 作為一個測試網路包處理能力的效能工具。syn攻擊的工具


pktgen是Linux核心自帶的高效能網路測試工具,pktgen命令不能直接找到,因為pktgen作為核心來執行,需要你載入pktgen核心模組後,再通過/proc檔案系統來互動。


modprobe pktgen


ls /proc/pktgen


pktgen 在每個 CPU 上啟動一個核心執行緒,並可以通過 /proc/net/pktgen 下面的同名檔案,跟這些執行緒互動;而 pgctrl 則主要用來控制這次測試的開啟和停止
注:如果 modprobe 命令執行失敗,說明你的核心沒有配置 CONFIG_NET_PKTGEN 選項。這就需要你配置 pktgen 核心模組(即 CONFIG_NET_PKTGEN=m)後,重新編譯核心,才可以使用。

使用 pktgen 測試網路效能時,需要先給每個核心執行緒 kpktgend_X 以及測試網絡卡,配置 pktgen 選項,然後再通過 pgctrl 啟動測試。

-------------------------------------------------------------------------
發包測試:
# 定義一個工具函式,方便後面配置各種測試選項
function pgset() {
local result
echo $1 > $PGDEV

result=`cat $PGDEV | fgrep "Result: OK:"`
if [ "$result" = "" ]; then
cat $PGDEV | fgrep Result:
fi
}

# 為 0 號執行緒繫結 eth0 網絡卡
PGDEV=/proc/net/pktgen/kpktgend_0
pgset "rem_device_all" # 清空網絡卡繫結
pgset "add_device eth0" # 新增 eth0 網絡卡

# 配置 eth0 網絡卡的測試選項
PGDEV=/proc/net/pktgen/eth0
pgset "count 1000000" # 總髮包數量
pgset "delay 5000" # 不同包之間的傳送延遲 (單位納秒)
pgset "clone_skb 0" # SKB 包複製
pgset "pkt_size 64" # 網路包大小
pgset "dst 192.168.0.30" # 目的 IP
pgset "dst_mac 11:11:11:11:11:11" # 目的 MAC

# 啟動測試
PGDEV=/proc/net/pktgen/pgctrl
pgset "start"

檢視測試報告:
cat /proc/net/pktgen/eth0

報告選項說明:
第一部分的 Params 是測試選項;

第二部分的 Current 是測試進度,其中, packts sofar(pkts-sofar)表示已經發送了100萬個包,也就表明測試已完成。

第三部分的 Result 是測試結果,包含測試所用時間、網路包數量和分片、PPS、吞吐量以及錯誤數。
---------------------------------------------------------------------------
注:pktgen在核心4.4中不存在,在核心3.10中存在

========================================================================================
TCP/UDP效能:


tcp/udp的效能測試工具:iperf/netperf
---------------------------------------------------------
開啟一個埠監聽連線:


# -s 表示啟動服務端,-i 表示彙報間隔,-p 表示監聽埠
$ iperf3 -s -i 1 -p 10000

在另一個視窗進行連線測試:
# -c 表示啟動客戶端,192.168.0.30 為目標伺服器的 IP
# -b 表示目標頻寬 (單位是 bits/s)
# -t 表示測試時間
# -P 表示併發數,-p 表示目標伺服器監聽埠


$ iperf3 -c 192.168.0.30 -b 1G -t 15 -P 2 -p 10000

---------------------------------------------------------

========================================================================================
HTTP效能:


http效能測試工具:ab
---------------------------------------------------------
# -c 表示併發請求數為 1000,-n 表示總的請求數為 10000
$ ab -c 1000 -n 10000 http://192.168.0.30/

---------------------------------------------------------
========================================================================================
應用負載效能:
wrk、TCPCopy、Jmeter 或者 LoadRunner命令確認應用程式的實際負載能力
---------------------------------------------------------
安裝wrk:
$ https://github.com/wg/wrk/tree/4.1.0
$ cd wrk
$ yum install build-essential -y
$ make
$ sudo cp wrk /usr/local/bin/

測試nginx效能:
# -c 表示併發連線數 1000,-t 表示執行緒數為 2
$ wrk -c 1000 -t 2 http://192.168.0.30/
---------------------------------------------------------
=======================================================================================
DNS域名解析:
nslookup time.geekbang.org

# +trace 表示開啟跟蹤查詢
# +nodnssec 表示禁止 DNS 安全擴充套件
$ dig +trace +nodnssec time.geekbang.org

-------------------------------------------------------------------
dnsmasq是最常用的DNS快取服務之一
DNS-dnsmasq安裝配置:
輕量級集合DNS,HTTP,TFTP軟體。
本初僅使用DNS功能。
用途 : 給本地區域網伺服器提供:hosts主機記錄,自定義域名,以及公網域名DNS轉發解析。
集中配置內網伺服器的hosts記錄,替代內網bind服務功能。

yum安裝
安裝
yum -y install dnsmasq
修改配置檔案
cp /etc/dnsmasq.conf /etc/dnsmasq.conf.bak
#重新填寫配置檔案 /etc/dnsmasq.conf

##偵聽埠
port=53
##服務啟動使用者及使用者組
user=nobody
group=nobody
##業務偵聽地址 - interface 選項和 listen-address 選項可以同時使用
listen-address=10.10.10.10,127.0.0.1
##不載入本地的 /etc/hosts 檔案
no-hosts
##新增讀取額外的 hosts 檔案路徑,可以多次指定。如果指定為目錄,則讀取目錄中的所有檔案。
addn-hosts=/data/dnsmasq/dnsmasq.hosts
##讀取目錄中的所有檔案,檔案更新將自動讀取
hostsdir=/data/dnsmasq/dnsmasq.d
##記錄dns查詢日誌,如果指定 log-queries=extra 那麼在每行開始處都有額外的日誌資訊。
log-queries
##設定日誌記錄器
log-facility=/data/dnsmasq/log/dnsmasq.log
##非同步log,緩解阻塞,提高效能。預設為5,最大100。
log-async=50
##指定 EDNS.0 UDP 包的最大尺寸,預設為 RFC5625 推薦的 edns-packet-max=4096
edns-packet-max=4096
##指定介面
interface=ens33
##指定不提供 DHCP 或 TFTP 服務的介面,僅提供 DNS 服務。
no-dhcp-interface=ens33
##指定 resolv-file 檔案路徑(上游DNS伺服器),預設/etc/resolv.dnsmasq
resolv-file=/data/dnsmasq/resolv.dnsmasq
##嚴格按照resolv.conf中的順序進行查詢
strict-order
##重啟後清空快取
clear-on-reload
##完整的域名才向上遊伺服器查詢,如果僅僅是主機名僅查詢hosts檔案
domain-needed
##快取條數,預設為150條,cache-size=0 禁用快取。
cache-size=1000
##不快取未知域名快取,預設情況下dnsmasq快取未知域名並直接返回為客戶端。
no-negcache
##指定DNS同屬查詢轉發數量
dns-forward-max=1000
建立相關配置檔案及資料夾
mkdir -p /data/dnsmasq/{dnsmasq.d,log}
touch /data/dnsmasq/{dnsmasq.hosts,resolv.dnsmasq}
填寫DNS轉發伺服器(提供非自定義域名查詢)
#新增配置 /data/dnsmasq/resolv.dnsmasq
nameserver 223.5.5.5
nameserver 1.2.4.8
填寫hosts主機記錄(提供域名hosts記錄集中查詢)
#新增配置 /data/dnsmasq/dnsmasq.hosts
10.10.10.10 test10
10.10.10.11 test11
10.10.10.12 test12
修改addn-hosts指定hosts記錄檔案,需重啟dnsmasq,可以通過hostsdir指定域名配置檔案新增解析。

填寫自定義域名(提供內網自定義域名查詢)
#新增配置檔案 /data/dnsmasq/dnsmasq.d/k8s.test (為方便區分不同的二級域名,建議按二級域名建立配置檔案)
10.10.10.11 etcd.k8s.test
啟動服務並設定開機啟動
systemctl start dnsmasq.service
systemctl enable dnsmasq.service
所有伺服器設定DNS指向10.10.10.10
#修改配置項 /etc/sysconfig/network-scripts/ifcfg-eth0
PEERDNS=no #拒絕接受DHCP分發的DNS配置
DNS1=10.10.10.10 #自定義配置DNS伺服器地址

#重啟網路配置
systemctl restart network.service
其他DNS用法
新增指定泛域名通過指定DNS伺服器解析(防域名被劫持,或者轉發指定域名解析)
#增加配置 /etc/dnsmasp.conf
server=/sohu.com/10.1.1.1
新增指定泛域名解析成指定IP (可用來遮蔽特定的域名)
#增加配置 /etc/dnsmasp.conf
address=/baidu.com/2.2.2.2
新增A記錄
#增加配置 /etc/dnsmasp.conf
host-record=test13.test,10.10.10.13
新增別名記錄(需要先新增源地址解析記錄,在新增別名記錄)
#增加配置 /data/dnsmasq/dnsmasq.d/test.test
10.10.10.20 20.test.test
#增加配置 /etc/dnsmasp.conf
cname=10.test.test,20.test.test
-------------------------------------------------------------------------
nslookup -type=PTR 35.190.27.188 8.8.8.8 #PTR反解析
==================================================================================
# -w 表示只輸出 HTTP 狀態碼及總時間,-o 表示將響應重定向到 /dev/null
$ curl -s -w 'Http code: %{http_code}\nTotal time:%{time_total}s\n' -o /dev/null http://192.168.0.30/

DDOS模擬與防護:
hping3模擬DDOS攻擊:
# -S 引數表示設定 TCP 協議的 SYN(同步序列號),-p 表示目的埠為 80
# -i u10 表示每隔 10 微秒傳送一個網路幀
$ hping3 -S -p 80 -i u10 192.168.0.30

iptables限制syn連線數:


# 限制 syn 併發數為每秒 1 次
$ iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT

# 限制單個 IP 在 60 秒新建立的連線數為 10
$ iptables -I INPUT -p tcp --dport 80 --syn -m recent --name SYN_FLOOD --update --seconds 60 --hitcount 10 -j REJECT

#半連線容量配置:
檢視:
$ sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 256

配置:
$ sysctl -w net.ipv4.tcp_max_syn_backlog=1024
net.ipv4.tcp_max_syn_backlog = 1024

更改連線重試次數:
$ sysctl -w net.ipv4.tcp_synack_retries=1
net.ipv4.tcp_synack_retries = 1

#開啟SYN Cookies用於處理相同請求的連線
$ sysctl -w net.ipv4.tcp_syncookies=1
net.ipv4.tcp_syncookies = 1

#以上配置的永久生效配置:


$ cat /etc/sysctl.conf


net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_max_syn_backlog = 1024

sysctl -p
==================================================================================
hping3測試網路延遲:
hping3 -c 3 -S -p 80 baidu.com
# -c 表示傳送 3 次請求,-S 表示設定 TCP SYN,-p 表示埠號為 80

traceroute測試網路延遲:
# --tcp 表示使用 TCP 協議,-p 表示埠號,-n 表示不對結果中的 IP 地址執行反向域名解析
traceroute --tcp -p 80 -n baidu.com

hping3 -c 3 -S -p 8080 192.168.0.30 #hping3壓力測試

tcpdump -nn tcp port 8080 -w nginx.pcap #tcpdump抓測試包儲存

wrk --latency -c 100 -t 2 --timeout 2 http://192.168.0.30:8080/ #測試網路延遲

strace -f wrk --latency -c 100 -t 2 --timeout 2 http://192.168.0.30:8080/ #strace命令追蹤命令的系統呼叫

注:大量小包傳送,可能與tcp演算法有關,Nagle演算法,是TCP協議中用於減少小包傳送數量的一種優化演算法,目的是為了提高實際頻寬的利用率。tcp_nodelay off;nginx中這個關閉了,就會每次請求的包都會發送。開啟後可以提高發送效率。

=================================================================================
NAT原理:


靜態NAT,即內網ip與公網ip是一對一的永久對映關係;


動態NAT,即內網ip從公網ip池中,動態選擇一個進行對映;


網路地址埠轉換NAPT,即把內網ip對映到公網ip的不同埠上,讓多個內網ip可以共享一個公網ip地址。

三類NAPT:


第一類是源地址轉換 SNAT,即目的地址不變,只替換源 IP 或源埠。SNAT 主要用於,多個內網 IP 共享同一個公網 IP ,來訪問外網資源的場景

第二類是目的地址轉換 DNAT,即源 IP 保持不變,只替換目的 IP 或者目的埠。DNAT 主要通過公網 IP 的不同埠號,來訪問內網的多種服務,同時會隱藏後端伺服器的真實 IP 地址。

第三類是雙向地址轉換,即同時使用 SNAT 和 DNAT。當接收到網路包時,執行 DNAT,把目的 IP 轉換為內網 IP;而在傳送網路包時,執行 SNAT,把源 IP 替換為外部 IP。

雙向地址轉換,其實就是外網 IP 與內網 IP 的一對一對映關係,所以常用在虛擬化環境中,為虛擬機器分配浮動的公網 IP 地址。

iptables包括 filter(用於過濾)、nat(用於 NAT)、mangle(用於修改分組資料) 和 raw(用於原始資料包)等。

SNAT轉換的兩種方式:
$ iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -j MASQUERADE
$ iptables -t nat -A POSTROUTING -s 192.168.0.2 -j SNAT --to-source 100.100.100.100

DNAT轉換:
$ iptables -t nat -A PREROUTING -d 100.100.100.100 -j DNAT --to-destination 192.168.0.2

雙向地址轉換:
$ iptables -t nat -A POSTROUTING -s 192.168.0.2 -j SNAT --to-source 100.100.100.100
$ iptables -t nat -A PREROUTING -d 100.100.100.100 -j DNAT --to-destination 192.168.0.2

開啟Linux轉發功能:
$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

$ sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

$ cat /etc/sysctl.conf | grep ip_forward
net.ipv4.ip_forward=1

=======================================================================================
systemtap工具:
SystemTap 是 Linux 的一種動態追蹤框架,它把使用者提供的指令碼,轉換為核心模組來執行,用來監測和跟蹤核心的行為。

---------------------------------------------
systemtap安裝:
# Ubuntu
apt-get install -y systemtap-runtime systemtap
# Configure ddebs source
echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse
deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \
sudo tee -a /etc/apt/sources.list.d/ddebs.list
# Install dbgsym
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F2EDC64DC5AEE1F6B9C621F0C8CAB6595FDFF622
apt-get update
apt install ubuntu-dbgsym-keyring
stap-prep
apt-get install linux-image-`uname -r`-dbgsym

# CentOS
yum install systemtap kernel-devel yum-utils kernel
stab-prep
---------------------------------------------

NAT正常工作至少需要兩個步驟:
第一,利用 Netfilter 中的鉤子函式(Hook),修改源地址或者目的地址。

第二,利用連線跟蹤模組 conntrack ,關聯同一個連線的請求和響應。

寫systemtap的stap指令碼:(不懂)
---------------------------------------------
#! /usr/bin/env stap

############################################################
# Dropwatch.stp
# Author: Neil Horman <[email protected]>
# An example script to mimic the behavior of the dropwatch utility
# http://fedorahosted.org/dropwatch
############################################################

# Array to hold the list of drop points we find
global locations

# Note when we turn the monitor on and off
probe begin { printf("Monitoring for dropped packets\n") }
probe end { printf("Stopping dropped packet monitor\n") }

# increment a drop counter for every location we drop at
probe kernel.trace("kfree_skb") { locations[$location] <<< 1 }

# Every 5 seconds report our drop locations
probe timer.sec(5)
{
printf("\n")
foreach (l in locations-) {
printf("%d packets dropped at %s\n",
@count(locations[l]), symname(l))
}
delete locations
}

注:這個指令碼,跟蹤核心函式 kfree_skb() 的呼叫,並統計丟包的位置
------------------------------------------------

執行stap命令:stap是systemtap的命令列工具
$ stap --all-modules dropwatch.stp

執行ab命令,可以在stap命令監控端看到追蹤的輸出
$ ab -c 5000 -n 10000 -r -s 30 http://192.168.0.30:8080/

通過perf record和perf report命令可以記錄ab壓測的呼叫
perf record -a -g -- sleep 30
perf report -g graph,0

# 統計總的連線跟蹤數
$ conntrack -L -o extended | wc -l

# 統計 TCP 協議各個狀態的連線跟蹤數
$ conntrack -L -o extended | awk '/^.*tcp.*$/ {sum[$6]++} END {for(i in sum) print i, sum[i]}'

# 統計各個源 IP 的連線跟蹤數
$ conntrack -L -o extended | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10

===================================================================================
網路通訊各層效能指標:
1.首先是網路介面層和網路層,它們主要負責網路包的封裝、定址、路由,以及傳送和接收。每秒可處理的網路包數 PPS,就是它們最重要的效能指標(特別是在小包的情況下)。你可以用核心自帶的發包工具 pktgen ,來測試 PPS 的效能。

2.再向上到傳輸層的 TCP 和 UDP,它們主要負責網路傳輸。對它們而言,吞吐量(BPS)、連線數以及延遲,就是最重要的效能指標。你可以用 iperf 或 netperf ,來測試傳輸層的效能。

不過要注意,網路包的大小,會直接影響這些指標的值。所以,通常,你需要測試一系列不同大小網路包的效能。

3.最後,再往上到了應用層,最需要關注的是吞吐量(BPS)、每秒請求數以及延遲等指標。你可以用 wrk、ab 等工具,來測試應用程式的效能。


網路效能優化:


1.從網路io角度:
第一種是最常用的 I/O 多路複用技術 epoll,主要用來取代 select 和 poll。這其實是解決 C10K 問題的關鍵,也是目前很多網路應用預設使用的機制。

第二種是使用非同步 I/O(Asynchronous I/O,AIO)。AIO 允許應用程式同時發起很多 I/O 操作,而不用等待這些操作完成。等到 I/O 完成後,系統會用事件通知的方式,告訴應用程式結果。不過,AIO 的使用比較複雜,你需要小心處理很多邊緣情況。

2.從程序的工作模型:
第一種,主程序 + 多個 worker 子程序。其中,主程序負責管理網路連線,而子程序負責實際的業務處理。這也是最常用的一種模型。

第二種,監聽到相同埠的多程序模型。在這種模型下,所有程序都會監聽相同介面,並且開啟 SO_REUSEPORT 選項,由核心負責,把請求負載均衡到這些監聽程序中去。

3.應用層網路協議優化:
使用長連線取代短連線,可以顯著降低 TCP 建立連線的成本。在每秒請求次數較多時,這樣做的效果非常明顯。

使用記憶體等方式,來快取不常變化的資料,可以降低網路 I/O 次數,同時加快應用程式的響應速度。

使用 Protocol Buffer 等序列化的方式,壓縮網路 I/O 的資料量,可以提高應用程式的吞吐。

使用 DNS 快取、預取、HTTPDNS 等方式,減少 DNS 解析的延遲,也可以提升網路 I/O 的整體速度。

4.套接字角度


增大每個套接字的緩衝區大小 net.core.optmem_max;

增大套接字接收緩衝區大小 net.core.rmem_max 和傳送緩衝區大小 net.core.wmem_max;

增大 TCP 接收緩衝區大小 net.ipv4.tcp_rmem 和傳送緩衝區大小 net.ipv4.tcp_wmem。

注:
tcp_rmem 和 tcp_wmem 的三個數值分別是 min,default,max,系統會根據這些設定,自動調整 TCP 接收 / 傳送緩衝區的大小。

udp_mem 的三個數值分別是 min,pressure,max,系統會根據這些設定,自動調整 UDP 傳送緩衝區的大小。

套接字介面額外配置選項:
為 TCP 連線設定 TCP_NODELAY 後,就可以禁用 Nagle 演算法;

為 TCP 連線開啟 TCP_CORK 後,可以讓小包聚合成大包後再發送(注意會阻塞小包的傳送);

使用 SO_SNDBUF 和 SO_RCVBUF ,可以分別調整套接字傳送緩衝區和接收緩衝區的大小

5.tcp協議優化:


第一類,在請求數比較大的場景下,你可能會看到大量處於 TIME_WAIT 狀態的連線,它們會佔用大量記憶體和埠資源。這時,我們可以優化與 TIME_WAIT 狀態相關的核心選項,比如採取下面幾種措施。

增大處於 TIME_WAIT 狀態的連線數量 net.ipv4.tcp_max_tw_buckets ,並增大連線跟蹤表的大小 net.netfilter.nf_conntrack_max。

減小 net.ipv4.tcp_fin_timeout 和 net.netfilter.nf_conntrack_tcp_timeout_time_wait ,讓系統儘快釋放它們所佔用的資源。

開啟埠複用 net.ipv4.tcp_tw_reuse。這樣,被 TIME_WAIT 狀態佔用的埠,還能用到新建的連線中。

增大本地埠的範圍 net.ipv4.ip_local_port_range 。這樣就可以支援更多連線,提高整體的併發能力。

增加最大檔案描述符的數量。你可以使用 fs.nr_open 和 fs.file-max ,分別增大程序和系統的最大檔案描述符數;或在應用程式的 systemd 配置檔案中,配置 LimitNOFILE ,設定應用程式的最大檔案描述符數。

第二類,為了緩解 SYN FLOOD 等,利用 TCP 協議特點進行攻擊而引發的效能問題,你可以考慮優化與 SYN 狀態相關的核心選項,比如採取下面幾種措施。

增大 TCP 半連線的最大數量 net.ipv4.tcp_max_syn_backlog ,或者開啟 TCP SYN Cookies net.ipv4.tcp_syncookies ,來繞開半連線數量限制的問題(注意,這兩個選項不可同時使用)

減少 SYN_RECV 狀態的連線重傳 SYN+ACK 包的次數 net.ipv4.tcp_synack_retries。

第三類,在長連線的場景中,通常使用 Keepalive 來檢測 TCP 連線的狀態,以便對端連線斷開後,可以自動回收。但是,系統預設的 Keepalive 探測間隔和重試次數,一般都無法滿足應用程式的效能要求。所以,這時候你需要優化與 Keepalive 相關的核心選項,比如:

縮短最後一次資料包到 Keepalive 探測包的間隔時間 net.ipv4.tcp_keepalive_time;

縮短髮送 Keepalive 探測包的間隔時間 net.ipv4.tcp_keepalive_intvl;

減少 Keepalive 探測失敗後,一直到通知應用程式前的重試次數 net.ipv4.tcp_keepalive_probes。

6.udp協議優化:
UDP 提供了面向資料報的網路協議,它不需要網路連線,也不提供可靠性保障。所以,UDP 優化,相對於 TCP 來說,要簡單得多。這裡我也總結了常見的幾種優化方案

跟上篇套接字部分提到的一樣,增大套接字緩衝區大小以及 UDP 緩衝區範圍;

跟前面 TCP 部分提到的一樣,增大本地埠號的範圍;

根據 MTU 大小,調整 UDP 資料包的大小,減少或者避免分片的發生。

7.網路層優化:


網路層,負責網路包的封裝、定址和路由,包括 IP、ICMP 等常見協議。在網路層,最主要的優化,其實就是對路由、 IP 分片以及 ICMP 等進行調優。

第一種,從路由和轉發的角度出發,你可以調整下面的核心選項。

在需要轉發的伺服器中,比如用作 NAT 閘道器的伺服器或者使用 Docker 容器時,開啟 IP 轉發,即設定 net.ipv4.ip_forward = 1。

調整資料包的生存週期 TTL,比如設定 net.ipv4.ip_default_ttl = 64。注意,增大該值會降低系統性能。

開啟資料包的反向地址校驗,比如設定 net.ipv4.conf.eth0.rp_filter = 1。這樣可以防止 IP 欺騙,並減少偽造 IP 帶來的 DDoS 問題。

第二種,從分片的角度出發,最主要的是調整 MTU(Maximum Transmission Unit)的大小。


通常,MTU 的大小應該根據乙太網的標準來設定。乙太網標準規定,一個網路幀最大為 1518B,那麼去掉乙太網頭部的 18B 後,剩餘的 1500 就是乙太網 MTU 的大小。

在使用 VXLAN、GRE 等疊加網路技術時,要注意,網路疊加會使原來的網路包變大,導致 MTU 也需要調整。

比如,就以 VXLAN 為例,它在原來報文的基礎上,增加了 14B 的乙太網頭部、 8B 的 VXLAN 頭部、8B 的 UDP 頭部以及 20B 的 IP 頭部。換句話說,每個包比原來增大了 50B。

所以,我們就需要把交換機、路由器等的 MTU,增大到 1550, 或者把 VXLAN 封包前(比如虛擬化環境中的虛擬網絡卡)的 MTU 減小為 1450。

另外,現在很多網路裝置都支援巨幀,如果是這種環境,你還可以把 MTU 調大為 9000,以提高網路吞吐量。

第三種,從 ICMP 的角度出發,為了避免 ICMP 主機探測、ICMP Flood 等各種網路問題,你可以通過核心選項,來限制 ICMP 的行為。

比如,你可以禁止 ICMP 協議,即設定 net.ipv4.icmp_echo_ignore_all = 1。這樣,外部主機就無法通過 ICMP 來探測主機。

或者,你還可以禁止廣播 ICMP,即設定 net.ipv4.icmp_echo_ignore_broadcasts = 1

8.鏈路層優化:


網路層的下面是鏈路層,所以最後,我們再來看鏈路層的優化方法。

鏈路層負責網路包在物理網路中的傳輸,比如 MAC 定址、錯誤偵測以及通過網絡卡傳輸網路幀等。自然,鏈路層的優化,也是圍繞這些基本功能進行的。接下來,我們從不同的幾個方面分別來看。

由於網絡卡收包後呼叫的中斷處理程式(特別是軟中斷),需要消耗大量的 CPU。所以,將這些中斷處理程式排程到不同的 CPU 上執行,就可以顯著提高網路吞吐量。這通常可以採用下面兩種方法。

比如,你可以為網絡卡硬中斷配置 CPU 親和性(smp_affinity),或者開啟 irqbalance 服務。

再如,你可以開啟 RPS(Receive Packet Steering)和 RFS(Receive Flow Steering),將應用程式和軟中斷的處理,排程到相同 CPU 上,這樣就可以增加 CPU 快取命中率,減少網路延遲。

另外,現在的網絡卡都有很豐富的功能,原來在核心中通過軟體處理的功能,可以解除安裝到網絡卡中,通過硬體來執行。

TSO(TCP Segmentation Offload)和 UFO(UDP Fragmentation Offload):在 TCP/UDP 協議中直接傳送大包;而 TCP 包的分段(按照 MSS 分段)和 UDP 的分片(按照 MTU 分片)功能,由網絡卡來完成 。

GSO(Generic Segmentation Offload):在網絡卡不支援 TSO/UFO 時,將 TCP/UDP 包的分段,延遲到進入網絡卡前再執行。這樣,不僅可以減少 CPU 的消耗,還可以在發生丟包時只重傳分段後的包。

LRO(Large Receive Offload):在接收 TCP 分段包時,由網絡卡將其組裝合併後,再交給上層網路處理。不過要注意,在需要 IP 轉發的情況下,不能開啟 LRO,因為如果多個包的頭部資訊不一致,LRO 合併會導致網路包的校驗錯誤。

GRO(Generic Receive Offload):GRO 修復了 LRO 的缺陷,並且更為通用,同時支援 TCP 和 UDP。

RSS(Receive Side Scaling):也稱為多佇列接收,它基於硬體的多個接收佇列,來分配網路接收程序,這樣可以讓多個 CPU 來處理接收到的網路包。

VXLAN 解除安裝:也就是讓網絡卡來完成 VXLAN 的組包功能。

最後,對於網路介面本身,也有很多方法,可以優化網路的吞吐量。

比如,你可以開啟網路介面的多佇列功能。這樣,每個佇列就可以用不同的中斷號,排程到不同 CPU 上執行,從而提升網路的吞吐量。

再如,你可以增大網路介面的緩衝區大小,以及佇列長度等,提升網路傳輸的吞吐量(注意,這可能導致延遲增大)。

你還可以使用 Traffic Control 工具,為不同網路流量配置 QoS。

9.C10M情況優化:
第一種,使用 DPDK 技術,跳過核心協議棧,直接由使用者態程序用輪詢的方式,來處理網路請求。同時,再結合大頁、CPU 繫結、記憶體對齊、流水線併發等多種機制,優化網路包的處理效率。

第二種,使用核心自帶的 XDP 技術,在網路包進入核心協議棧前,就對其進行處理,這樣也可以實現很好的效能。

===========================================================================================================================================
關於網路緩衝區的說法:
網絡卡收發網路包時,通過 DMA 方式互動的環形緩衝區;

網絡卡中斷處理程式為網路幀分配的,核心資料結構 sk_buff 緩衝區;

應用程式通過套接字介面,與網路協議棧互動時的套接字緩衝區。

環形緩衝區,由於需要 DMA 與網絡卡互動,理應屬於網絡卡裝置驅動的範圍。

sk_buff 緩衝區,是一個維護網路幀結構的雙向連結串列,連結串列中的每一個元素都是一個網路幀(Packet)。雖然 TCP/IP 協議棧分了好幾層,但上下不同層之間的傳遞,實際上只需要操作這個資料結構中的指標,而無需進行資料複製。

套接字緩衝區,則允許應用程式,給每個套接字配置不同大小的接收或傳送緩衝區。應用程式傳送資料,實際上就是將資料寫入緩衝區;而接收資料,其實就是從緩衝區中讀取。至於緩衝區中資料的進一步處理,則由傳輸層的 TCP 或 UDP 協議來完成。

sk_buff、套接字緩衝、連線跟蹤等,都通過 slab 分配器來管理。你可以直接通過 /proc/slabinfo,來檢視它們佔用的記憶體大小。

提高Linux系統下的負載能力,可以使用nginx等原生併發處理能力就很強的web伺服器,如果使用Apache的可以啟用其Worker模式,來提高其併發處理能力。除此之外,在考慮節省成本的情況下,可以修改Linux的核心相關TCP引數,來最大的提高伺服器效能。

TIME_WAIT

Linux系統下,TCP連線斷開後,會以TIME_WAIT狀態保留一定的時間,然後才會釋放埠。當併發請求過多的時候,就會產生大量的TIME_WAIT狀態的連線,無法及時斷開的話,會佔用大量的埠資源和伺服器資源。這個時候我們可以優化TCP的核心引數,來及時將TIME_WAIT狀態的埠清理掉。

本文介紹的方法只對擁有大量TIME_WAIT狀態的連線導致系統資源消耗有效,如果不是這種情況下,效果可能不明顯。可以使用netstat命令去查TIME_WAIT狀態的連線狀態,輸入下面的組合命令,檢視當前TCP連線的狀態和對應的連線數量:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'


用vim開啟配置檔案:#vim /etc/sysctl.conf這個命令會輸出類似下面的結果:


LAST_ACK 16
SYN_RECV 348
ESTABLISHED 70
FIN_WAIT1 229
FIN_WAIT2 30
CLOSING 33
TIME_WAIT 18098
我們只用關心TIME_WAIT的個數,在這裡可以看到,有18000多個TIME_WAIT,這樣就佔用了18000多個埠。要知道埠的數量只有65535個,佔用一個少一個,會嚴重的影響到後繼的新連線。這種情況下,我們就有必要調整下Linux的TCP核心引數,讓系統更快的釋放TIME_WAIT連線。

在這個檔案中,加入下面的幾行內容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

輸入下面的命令,讓核心引數生效:#sysctl -p

簡單的說明上面的引數的含義:

net.ipv4.tcp_syncookies = 1
#表示開啟SYN Cookies。當出現SYN等待佇列溢位時,啟用cookies來處理,可防範少量SYN攻擊,預設為0,表示關閉;


net.ipv4.tcp_tw_reuse = 1
#表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線,預設為0,表示關閉;


net.ipv4.tcp_tw_recycle = 1
#表示開啟TCP連線中TIME-WAIT sockets的快速回收,預設為0,表示關閉;


net.ipv4.tcp_fin_timeout
#修改系統預設的 TIMEOUT 時間。

在經過這樣的調整之後,除了會進一步提升伺服器的負載能力之外,還能夠防禦小流量程度的DoS、CC和SYN攻擊。

此外,如果你的連線數本身就很多,我們可以再優化一下TCP的可使用埠範圍,進一步提升伺服器的併發能力。依然是往上面的引數檔案中,加入下面這些配置:
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000


#這幾個引數,建議只在流量非常大的伺服器上開啟,會有顯著的效果。一般的流量小的伺服器上,沒有必要去設定這幾個引數。

net.ipv4.tcp_keepalive_time = 1200
#表示當keepalive起用的時候,TCP傳送keepalive訊息的頻度。預設是2小時,改為20分鐘。


net.ipv4.ip_local_port_range = 10000 65000
#表示用於向外連線的埠範圍。預設情況下很小:32768到61000,改為10000到65000。(注意:這裡不要將最低值設的太低,否則可能會佔用掉正常的埠!)


net.ipv4.tcp_max_syn_backlog = 8192
#表示SYN佇列的長度,預設為1024,加大佇列長度為8192,可以容納更多等待連線的網路連線數。


net.ipv4.tcp_max_tw_buckets = 6000
#表示系統同時保持TIME_WAIT的最大數量,如果超過這個數字,TIME_WAIT將立刻被清除並列印警告資訊。默 認為180000,改為6000。對於Apache、Nginx等伺服器,上幾行的引數可以很好地減少TIME_WAIT套接字數量,但是對於Squid,效果卻不大。此項引數可以控制TIME_WAIT的最大數量,避免Squid伺服器被大量的TIME_WAIT拖死。

核心其他TCP引數說明:


net.ipv4.tcp_max_syn_backlog = 65536
#記錄的那些尚未收到客戶端確認資訊的連線請求的最大值。對於有128M記憶體的系統而言,預設值是1024,小記憶體的系統則是128。


net.core.netdev_max_backlog = 32768
#每個網路介面接收資料包的速率比核心處理這些包的速率快時,允許送到佇列的資料包的最大數目。


net.core.somaxconn = 32768
#web應用中listen函式的backlog預設會給我們核心引數的net.core.somaxconn限制到128,而nginx定義的NGX_LISTEN_BACKLOG預設為511,所以有必要調整這個值。

net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216 #最大socket讀buffer,可參考的優化值:873200
net.core.wmem_max = 16777216 #最大socket寫buffer,可參考的優化值:873200
net.ipv4.tcp_timestsmps = 0


#時間戳可以避免序列號的卷繞。一個1Gbps的鏈路肯定會遇到以前用過的序列號。時間戳能夠讓核心接受這種“異常”的資料包。這裡需要將其關掉。
net.ipv4.tcp_synack_retries = 2


#為了開啟對端的連線,核心需要傳送一個SYN並附帶一個迴應前面一個SYN的ACK。也就是所謂三次握手中的第二次握手。這個設定決定了核心放棄連線之前傳送SYN+ACK包的數量。

#net.ipv4.tcp_syn_retries = 2

#在核心放棄建立連線之前傳送SYN包的數量。
#net.ipv4.tcp_tw_len = 1


net.ipv4.tcp_tw_reuse = 1
# 開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線。

net.ipv4.tcp_wmem = 8192 436600 873200
# TCP寫buffer,可參考的優化值: 8192 436600 873200
net.ipv4.tcp_rmem = 32768 436600 873200
# TCP讀buffer,可參考的優化值: 32768 436600 873200
net.ipv4.tcp_mem = 94500000 91500000 92700000


# 同樣有3個值,意思是:
net.ipv4.tcp_mem[0]:低於此值,TCP沒有記憶體壓力。
net.ipv4.tcp_mem[1]:在此值下,進入記憶體壓力階段。
net.ipv4.tcp_mem[2]:高於此值,TCP拒絕分配socket。


上述記憶體單位是頁,而不是位元組。可參考的優化值是:786432 1048576 1572864

net.ipv4.tcp_max_orphans = 3276800
#系統中最多有多少個TCP套接字不被關聯到任何一個使用者檔案控制代碼上。如果超過這個數字,連線將即刻被複位並打印出警告資訊。
這個限制僅僅是為了防止簡單的DoS攻擊,不能過分依靠它或者人為地減小這個值,更應該增加這個值(如果增加了記憶體之後)。


net.ipv4.tcp_fin_timeout = 30
#如果套接字由本端要求關閉,這個引數決定了它保持在FIN-WAIT-2狀態的時間。對端可以出錯並永遠不關閉連線,甚至意外當機。預設值是60秒。2.2 核心的通常值是180秒,你可以按這個設定,但要記住的是,即使你的機器是一個輕載的WEB伺服器,也有因為大量的死套接字而記憶體溢位的風險,FIN- WAIT-2的危險性比FIN-WAIT-1要小,因為它最多隻能吃掉1.5K記憶體,但是它們的生存期長些。

經過這樣的優化配置之後,你的伺服器的TCP併發處理能力會顯著提高。以上配置僅供參考,用於生產環境請根據自己的實際情況。

大多數Linux發行版都定義了適當的緩衝區和其他TCP引數,可以通過修改這些引數來分配更多的記憶體,從而改進網路效能。

設定核心引數的方法是通過proc介面,也就是通過讀寫/proc中的值。幸運的是,sysctl可以讀取/etc/sysctl.conf中的值並根據需要填充/proc,這樣就能夠更輕鬆地管理這些引數。

下面展示了在網際網路伺服器上應用於Internet伺服器的一些比較激進的網路設定。

# Use TCP syncookies when needed
net.ipv4.tcp_syncookies = 1
# Enable TCP window scaling
net.ipv4.tcp_window_scaling = 1
# Increase TCP max buffer size
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# Increase linux autotuning TCP buffer limits
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65535 16777216
# Increase number of ports available
net.ipv4.ip_local_port_range = 1024 65000

將這些設定新增到/etc/sysctl.conf的現有內容中。

第一個設定啟用TCP SYN cookie。

當從客戶機發來新的TCP連線時,資料包設定了SYN位,伺服器就位這個半開的連線建立一個條目,並用一個SYN-ACK資料包進行響應。在正常操作中,遠端客戶機用一個ACK資料包進行響應,這回使得半開的連線轉換為全開的。

有一種稱為SYN氾濫(SYN flood)的網路攻擊,它使ACK資料包無法返回,導致伺服器用光記憶體空間,無法處理到來的連線。SYN cookie特性可以識別出這種情況,並使用一種優雅的方法保留佇列中的空間,大多數系統都預設啟用這個特性,但是確保配置這個特性更可靠。

第二個設定啟用TCP視窗伸縮

啟用TCP視窗伸縮可以使客戶機能夠以更高的速度下載資料。TCP允許在未從遠端端收到確認的情況下發送多個數據包,預設設定是最多64KB,在與延遲比較大的遠端客戶機進行通訊時這個設定可能不夠,視窗伸縮會在頭中啟用更多的位,從而增加視窗大小。

後面四個配置項增加TCP傳送和接收緩衝區

這使應用程式可以更快地丟掉它的資料,從而為另一個請求服務,還可以強化遠端客戶機在伺服器繁忙時傳送資料的能力。

最後一個配置項增加可用的本地埠數量

這樣就增加了可以同時服務的最大連線數量。

在下一次引導系統時,或者下一次執行sysctl -p /etc/sysctl.conf時,這些設定就會生效。

TCP/IP子系統的調優
所有的TCP/IP調優引數都位於/proc/sys/net目錄,例如下面是最重要的一些調優引數:

# 最大的TCP資料接收緩衝
/proc/sys/net/core/rmem_max


# 最大的TCP資料傳送緩衝
/proc/sys/net/core/wmem_max


# 時間戳在TCP的包頭增加12個位元組
/proc/sys/net/ipv4/tcp_timestamps


# 有選擇的應答
/proc/sys/net/ipv4/tcp_sack


# 支援更大的TCP視窗,如果TCP視窗最大超過65535,必須設定該數值為1
/proc/sys/net/ipv4/tcp_window_scaling


# 預設的接收視窗大小
rmem_default


# 接收視窗的最大大小
rmem_max


# 預設的傳送視窗大小
wmem_default


# 傳送視窗的最大大小
wmem_max

/proc目錄下的所有內容都是臨時性的,所以重啟系統後任務修改都會丟失

建議在系統啟動時自動修改TCP/IP引數,將下面程式碼增加到/etc/rc.local檔案中,然後儲存檔案,系統重新引導的時候回自動修改下面TCP/IP引數:

echo 256960 > /proc/sys/net/core/rmem_default
echo 256960 > /proc/sys/net/core/rmem_max
echo 256960 > /proc/sys/net/core/wmem_default
echo 256960 > /proc/sys/net/core/wmem_max
echo 0 > /proc/sys/net/ipv4/tcp_timestamps
echo 1 > /proc/sys/net/ipv4/tcp_sack
echo 1 > /proc/sys/net/ipv4/tcp_window_scaling

TCP/IP引數都是自解釋的,TCP視窗大小設定為256960,禁止TCP的時間戳(取消在每個資料包的頭中增加12位元組),支援更大的TCP視窗和TCP有選擇的應答。

上面數值的設定是根據網際網路連線和最大頻寬/延遲率來決定的。

另外一個方法:使用/etc/sysctl.conf在系統啟動時將引數設定成需要設定的值。

net.core.rmem_default = 256960
net.core.rmem_max = 256960
net.core.wmem_default = 256960
net.core.wmem_max = 256960
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1