無線用戶連接使用WiFi時有抱怨說,微信連接正常,但是有時候發送圖片甚至短消息時出現轉圈現象。刷抖音、騰訊視頻、看B站或是開啟瀏覽器時部分圖片打不開,開啟某一個視屏時一直轉圈等問題。
出現這類問題時,首先需要排除網絡問題,排除無線部分的干擾、有線部分的延遲。分為幾個部分進行調查
無線網絡檢查
1. 連接無線后,通過ping檢查無線到本地網關之間的丟包率、延遲、抖動等是否異常
(排除無線信道干擾,信號不好等因素導致的網絡問題)

2. 檢查DNS速度和應答
開啟網址時,首先通過DNS獲得目標IP地址,從而建立網絡連接。
有時候,網絡速度慢有可能時因為上一層DNS解析服務器響應慢導致頁面打開慢,可通過在網關設備上運行dig命令查看dns解析的響應時間, 如:

也可在終端上,抓取DNS報文計算請求與響應報文時間戳差來了解DNS響應狀況。
排除掉一些基本的網絡問題后,若問題沒能解決,就得從系統的角度去分析性能問題了。
有些時候,需要收集一些現場的計數(可能會直接找到問題所在),但大多數情況都需要做進一步的模擬測試,以確認問題所在。
現場信息收集
現場參數檢查:
1. 檢查session情況(做NAT的網關設備一般為linux或者openwrt系統)
查看nf_conntrack_count計數 --- 當前連接數
cat /proc/sys/net/netfilter/nf_conntrack_count
2. 查看nf_conntrack表最大連接數
cat /proc/sys/net/netfilter/nf_conntrack_max
可通過如下命令臨時修改:
sysctl -w net.netfilter.nf_conntrack_max=65536
echo 16384 > /sys/module/nf_conntrack/parameters/hashsize
注意:
hashsize = nf_conntrack_max / 4
或者:
sysctl -w net.ipv4.ip_conntrack_max=65536
3. 查看nf_conntrack的TCP連接記錄時間
cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
4. 通過內核參數查看命令,查看所有參數配置
sysctl -a | grep nf_conntrack
5. 查看具體的session信息
cat /proc/net/nf_conntrack
當獲得一些現場信息后,需要在實驗室環境構造類似的環境,模擬用戶session變化情況(出現問題時,很大可能是因為資源死鎖或限制導致了類似的性能,因此需要不斷變化端口、IP、協議等參數,來逐步確認問題所在)
環境模擬
在進行本地機器進行模擬測試時,可能需要調整一些系統參數,以便使作為輔測設備本身不成為瓶頸。如:
- 客戶端連接數限制
單個壓測客戶端(一個IP地址)能承載的并發連接數是有限制的(其實是TCP/UDP的端口數目限制),這個上限是 65535(一般1024下的端口作為系統使用,所以65535只是一個最大上限),不可能更多。
- File Descriptors 限制
每一個建立的連接在 Linux 上都可視為一個打開的文件,會占用一個 File Descriptor,所以 ulimit 內各種限制中跟并發連接數最相關的是進程最大能打開的 File Descriptor數量。
[root@kvmserver1 ~]# ulimit -n
1024
可根據設備內存狀況,修改該數值
cat /etc/security/limits.conf

如修改為
* soft nofile 10000
* hard nofile 10000
普通用戶默認使用的是 soft 限制,并且能夠通過 ulimit -n 修改 soft 限制到最大跟 hard 一樣,超過 hard 的話會報錯:
ulimit: open files: cannot modify limit: Operation not permitted
若使用的連接需要特別的多,可能會需要調整下面這幾個值。這幾個值限制文件 Handle最大數量都很大,默認為 1024 * 1024,但也能調整的更大:
fs.file-max = 1000000
fs.file-nr = 13920 0 1000000
fs.nr_open = 1048576
file-max 是 kernel 能分配的文件 Handle 最大數量
file-nar 有三個值,動態變化的,第一個值是當前已經分配的文件 Handle數量,第二個值是分配但是未使用的文件 Handle 數一般都是 0,最后一個實際就是 file-max 的值,表示系統最大分配的文件 Handle 數量。當系統內文件 Handle 數超過 file-max 后,一樣會報 Too many open files。
nr_open 是單個進程能分配的最大文件 Handle 數量,這個值一定比 file-max 小,并且一定要比 limits.conf 內的 soft nofile, hard nofile 大,不然 soft nofile, hard nofile 設置再大都沒用。

- 自動分配本地端口范圍
確定一個連接需要五個元素 Source IP + Source Port + Destination IP + Destination Port + 協議(TCP/UDP),一般客戶端在連服務端的時候只要獲取到服務端的 Destination IP 和 Destination Port 即可,Source IP 是客戶端自己的 IP,客戶端系統會自動分配一個 Source Port 來建立連接。而這個 Source Port 的選擇范圍是可通過 sysctl net.ipv4.ip_local_port_range 參數來定制的??梢詧绦幸幌逻@個命令來獲取當前系統的設定,例如:
sysctl -w net.ipv4.ip_local_port_range="15000 61000"
即表示在與 remote 服務建立連接時,系統只能自動從 15000 至 61000 中選擇一個作為 Local Port,也就是 Source Port。
如果希望壓測客戶端和服務器建立大量的連接,則需要將該范圍設置的大一些,給客戶端留足端口數(如 1024 - 65535),如果留的端口不足的話會報錯。
- 端口復用
TCP 連接斷開之后主動發起 FIN 的一方最終會進入 TIME_WAIT 狀態,處在這個狀態時連接之前所占用的端口不能被下一個新的連接使用,必須等待一段時間之后才能使用。如果是單獨測試并發連接峰值,減少 TIME_WAIT 連接數可能用處不大,但如果是連續的測試,每次關閉客戶端準備再來下一輪測試時必須等足 TIME_WAIT 時間,如果 TIME_WAIT 時間比較長就比較煩,所以減少 TIME WAIT 對測試有一定好處。因為一般壓測都是內網,所以 TIME WAIT 清理方面能稍微激進一些??煽紤]設置:
Client 開啟TCP Timestamps 后開啟 net.ipv4.tcp_tw_reuse 或 net.ipv4.tcp_tw_recycle,
將 net.ipv4.tcp_max_tw_buckets 設置的很小,TIME WAIT 連接超過該值后直接清理。因為一般測試都在內網,沒有 NAP 的情況下 Per-Host 的 Timestamp 配合 PAWS 一般能消除跨連接數據包錯誤到達問題。
通過如下命令可查看當前TIME_WAIT的數量
netstat -an | grep "TIME_WAIT" | wc -l
考慮壓測結束的時候由 Client 主動斷開連接,并且設置 SO_LINGER 為 0,斷開連接時候直接發 RST;
sysctl -w net.ipv4.tcp_timestamps=1
#開啟TCP時間戳
#以一種比重發超時更精確的方法(請參閱 RFC 1323)來啟用對 RTT 的計算;為了實現更好的性能應該啟用這個選項。
sysctl -w net.ipv4.tcp_tw_reuse=1
# 1 表示開啟重用。允許將TIME-WAIT sockets重新用于新的TCP連接,默認為0,表示關閉;
sysctl -w net.ipv4.tcp_tw_recycle=1
# 1 表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉。
sysctl -w net.ipv4.tcp_max_tw_buckets=5000
# 5000 表示系統同時保持TIME_WAIT套接字的最大數量,如果超過這個數字,TIME_WAIT套接字將立刻被清除并打印警告信息
sysctl -w net.ipv4.tcp_keepalive_time=1200
#1200 表示當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時,改為20分鐘,單位為秒。
net.ipv4.tcp_fin_timeout=30
#30表示如果套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間。單位為秒。
- tcp syn flood丟棄限制
在測試中,有時需要模擬大量的TCP連接,但并發連接數量多了,就會出現很多連接建立失敗,同時在Server會看到如下的一些日志打印
TCP: request_sock_TCP: Possible SYN flooding on port 45000. Sending cookies. Check SNMP counters
所謂的TCP SYN Flood的攻擊,其實就是利用TCP協議三次握手過程進行的攻擊:如果一個客戶端向另一個客戶端發起TCP連接時,需要先發送TCP SYN報文,對端收到報文后回應TCP SYN+ACK報文,發起方再發送TCP ACK,這樣握手成功,連接也就建立起來了。
具體實現時,當接收端收到SYN報文,回應SYN+ACK報文前,需要維護一個隊列(未連接隊列 ---表示收到了SYN, 狀態標識為SYN_RECV), 當收到對端的ACK報文時,從隊列中移除,進入ESTABLISHED狀態。
[root@centos8 ~]# sysctl -a | grep cookies
net.ipv4.tcp_syncookies = 1
#表示開啟CentOS SYN Cookies,這是個bool值。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量CentOS SYN攻擊,默認為0,表示關閉;
能夠有效防范SYN Flood攻擊的手段之一就是SYN Cookie。SYN Cookie原理由D. J. Bernstain和 Eric Schenk發明。SYN Cookie是對TCP服務器端的三次握手協議作一些修改,專門用來防范CentOS SYN Flood攻擊的一種手段,它的原理是, 在TCP服務器收到TCP SYN包并返回TCP SYN+ACK包時,不分配一個專門的數據區,而是根據這個SYN包計算出一個cookie值。在收到TCP ACK包時,TCP服務器在根據那個cookie值檢查這個TCP ACK包的合法性。如果合法,再分配專門的數據區進行處理未來的TCP連接
[root@centos8 ~]# sysctl -a | grep backlog
net.core.netdev_max_backlog = 1000
#該參數決定了,網絡設備接收數據包的速率比內核處理這些包的速率快時,允許送到隊列的數據包的最大數目。將數據包放入 CPU 的 backlog 的時候需要看隊列內當前積壓的數據包有多少,超過 net.core.netdev_max_backlog 后要丟棄數據
net.ipv4.tcp_max_syn_backlog = 256
#表示SYN隊列長度,默認1024,改成8192,可以容納更多等待連接的網絡連接數。
- 其他參數設置
net.core.netdev_max_backlog = 400000
#該參數決定了,網絡設備接收數據包的速率比內核處理這些包的速率快時,允許送到隊列的數據包的最大數目。
net.core.optmem_max = 10000000
#該參數指定了每個套接字所允許的最大緩沖區的大小
#指定了接收套接字緩沖區大小的缺省值(以字節為單位)。
對收消息過程來說,Socket 占用內存量就是 Receive Queue、Prequeue、Backlog、Out of order 隊列內排隊的 sk_buff 占用內存總數。當數據被拉取到 User Space 后,就不再占用 Socket 的內存。這里有幾個需要注意的,一個是發送過程和接收過程共用分配的 Socket 內存總量 sk_forward_alloc。對收消息過程來說,Receive Queue、Prequeue、Backlog、Out of order 共同占用的內存量不能超過 sk->sk_rcvbuf。如果用戶進程處理消息較慢,大量消息在 Receive Queue、Prequeue、Backlog 內排隊,則 Out of order 隊列的大小會受到限制,而 Out of order 隊列大小會影響 TCP Receive Window 的大小,從而在用戶進程處理消息慢的時候能通過減小 Receive Window 讓對端減慢發消息速度。一般來說 Socket 的 sk_rcvbuf 受到如下兩個配置的控制:
sysctl -w net.core.rmem_max=8388608
sysctl -w net.core.rmem_default=8388608
net.core.somaxconn = 100000
#Linux kernel參數,表示socket監聽的backlog(監聽隊列)上限
net.core.wmem_default = 11059200
#定義默認的發送窗口大??;對于更大的 BDP 來說,這個大小也應該更大。
net.core.wmem_max = 11059200
#定義發送窗口的最大大?。粚τ诟蟮?BDP 來說,這個大小也應該更大。
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
#嚴謹模式 1 (推薦)
#松散模式 0
net.ipv4.tcp_congestion_control = reno cubic
#默認推薦設置是 htcp
net.ipv4.tcp_window_scaling = 0
#關閉tcp_window_scaling
#啟用 RFC 1323 定義的 window scaling;要支持超過 64KB 的窗口,必須啟用該值。
net.ipv4.tcp_ecn = 0
#把TCP的直接擁塞通告(tcp_ecn)關掉
net.ipv4.tcp_sack = 1
#關閉tcp_sack
#啟用有選擇的應答(Selective Acknowledgment),
#這可以通過有選擇地應答亂序接收到的報文來提高性能(這樣可以讓發送者只發送丟失的報文段);
#(對于廣域網通信來說)這個選項應該啟用,但是這會增加對 CPU 的占用。
net.ipv4.tcp_max_tw_buckets = 10000
#表示系統同時保持TIME_WAIT套接字的最大數量
net.ipv4.tcp_keepalive_probes = 3
#如果對方不予應答,探測包的發送次數
net.ipv4.tcp_keepalive_intvl = 15
#keepalive探測包的發送間隔
對于 TCP 連接來說稍微特別一些,除了 sk_rcvbuf 的限制之外,TCP 還有自己的一套 Socket 接收 Buffer 的限制機制,能根據系統當前所有 TCP 連接占用的總內存量判斷系統壓力級別,來決定是否能為某個 Socket 繼續分配接收 Buffer。這里要區分清楚的是 sk_rcvbuf 是 Socket 接收 buffer 分配的上限,而 Socket 當前實際分配的接收 buffer 大小是 sk_rmem_alloc 記錄。連接每次收到一個 sk_buff 放入 Socket 隊列之后,就會增加 sk_rmem_alloc 并減少 sk_forward_alloc 的值,sk_forward_alloc 不夠的時候就需要向系統申請配額。如果系統上只有一個連接,那 Socket 分配的接收 Buffer 沒有達到 sk_rcvbuf 之前,系統可能都會允許給這個連接繼續分配接收 buffer。但是如果系統上有幾百萬連接,占用了大量的內存,每個連接都分為 sk_rcvbuf 這么多接收 Buffer 的話系統可能會支撐不住,所以 TCP 的接收 Buffer 的限制機制就是在 Socket 的接收 Buffer 還未到達 sk_rcvbuf 之前就根據當前系統負載情況,在負載特別大的時候拒絕 Socket 擴大接收 buffer 的申請。
跟 tcp 連接的這個接收 Buffer 限制機制相關的配置是 net.ipv4.tcp_rmem ,是個數組,有三個值分別是 min, default, max,給 TCP Socket 分配 sk_rcvbuf 時會根據系統當前壓力級別從 min, default, max 三個值中選擇,用以控制 Socket 接收 Buffer 的大小。
net.ipv4.tcp_mem
#確定 TCP 棧應該如何反映內存使用;每個值的單位都是內存頁(通常是 4KB)。
#第一個值是內存使用的下限。
#第二個值是內存壓力模式開始對緩沖區使用應用壓力的上限。
#第三個值是內存上限。在這個層次上可以將報文丟棄,從而減少對內存的使用。對于較大的 BDP 可以增大這些值(但是要記住,其單位是內存頁,而不是字節)。
net.ipv4.tcp_rmem
#與 tcp_wmem 類似,不過它表示的是為自動調優所使用的接收緩沖區的值。
net.ipv4.tcp_wmem = 30000000 30000000 30000000
#為自動調優定義每個 socket 使用的內存。
#第一個值是為 socket 的發送緩沖區分配的最少字節數。
#第二個值是默認值(該值會被 wmem_default 覆蓋),緩沖區在系統負載不重的情況下可以增長到這個值。
#第三個值是發送緩沖區空間的最大字節數(該值會被 wmem_max 覆蓋)。
net.ipv4.tcp_slow_start_after_idle = 0
#關閉tcp的連接傳輸的慢啟動,即先休止一段時間,再初始化擁塞窗口。
net.ipv4.route.gc_timeout = 100
#路由緩存刷新頻率,當一個路由失敗后多長時間跳到另一個路由,默認是300。
net.ipv4.tcp_syn_retries = 1
#在內核放棄建立連接之前發送SYN包的數量。
net.ipv4.icmp_echo_ignore_broadcasts = 1
# 避免放大攻擊
net.ipv4.icmp_ignore_bogus_error_responses = 1
# 開啟惡意icmp錯誤消息保護
net.inet.udp.checksum=1
#防止不正確的udp包的攻擊
net.ipv4.conf.default.accept_source_route = 0
#是否接受含有源路由信息的ip包。參數值為布爾值,1表示接受,0表示不接受。
#在充當網關的linux主機上缺省值為1,在一般的linux主機上缺省值為0。
#從安全性角度出發,建議關閉該功能。