優(yōu)實戰(zhàn):從1000到90000連接的Linux內(nèi)核參數(shù)與自動化測試)
簡介一份測試開發(fā)崗位的實習(xí)報告完整記錄了實習(xí)生參與運輸網(wǎng)站安全項目上線的全過程。報告從實習(xí)目的與背景講起詳細(xì)展開需求評審、需求分析、測試計劃、用例設(shè)計、測試環(huán)境搭建、執(zhí)行測試、BUG跟蹤、測試報告八個核心環(huán)節(jié)并梳理了白盒/黑盒/灰盒、靜態(tài)/動態(tài)、單元/集成/系統(tǒng)等測試方法分類以及BUG提交單的構(gòu)成要素和實操壓力測試案例。適合軟件測試初學(xué)者、計算機專業(yè)學(xué)生及準(zhǔn)備測試開發(fā)實習(xí)的人群參考。資源為單個doc文檔共1個文件壓縮包大小24KB目前已有442人學(xué)習(xí)下載。內(nèi)容結(jié)構(gòu)完整包含真實項目測試流程、壓力測試中Linux文件句柄限制的排錯思路等能為讀者提供測試開發(fā)崗位的實際工作認(rèn)知和實習(xí)報告撰寫參考。1. 壓力測試從 1000 到 90000測試開發(fā)實習(xí)里最能打的一段調(diào)優(yōu)經(jīng)歷第一天上手壓力測試我以為把客戶端連接數(shù)開到 10 萬就能測出服務(wù)器上限。結(jié)果客戶端跑起來不到兩秒連接數(shù)停在 1000 出頭不再上漲——不是服務(wù)器扛不住并發(fā)而是 Linux 默認(rèn)只給當(dāng)前用戶開了 1024 個文件描述符扣掉標(biāo)準(zhǔn)輸入輸出和服務(wù)端監(jiān)聽 socket 占用的那十來個真正能分給客戶端連接的只剩 1014 個。測試開發(fā)崗位的第一個完整任務(wù)就是把這 1014 個連接一路調(diào)到 9 萬多。這篇文章把這段經(jīng)歷完整拆開從測試全流程、Bug 單怎么寫到壓力測試?yán)镂募枋龇?、端口范圍、TIME_WAIT 三層限制的逐級突破最后落到 pytest 自動化登錄測試的落地方式。想做測試開發(fā)的新手可以跟著走一遍寫過一段時間用例的人也能在參數(shù)細(xì)節(jié)里翻到一些值得對照的東西。2. 測試開發(fā)日常工作流需求評審、用例設(shè)計到 Bug 單落地的關(guān)鍵細(xì)節(jié)2.1 測試流程八階段每個階段回答什么問題產(chǎn)出什么正式接手壓測之前先跟著測試主管完整走了一遍運輸網(wǎng)站上線前的測試流程。看起來是八個階段但每個階段其實都在回答一個具體問題產(chǎn)物也不只是文檔而是下一階段的輸入。把這個問題和產(chǎn)出物對齊流程才不會變成形式。階段要回答的問題核心產(chǎn)出需求評審需求文檔是否自洽有沒有描述不清或互相矛盾的地方評審結(jié)論與問題清單需求分析目標(biāo)系統(tǒng)必須完成哪些工作范圍邊界在哪功能清單、需求分析說明測試計劃需要多少人力、設(shè)備時間怎么排功能點如何劃分測試計劃、資源排期用例設(shè)計哪些輸入組合能覆蓋功能點和邊界條件測試用例集測試環(huán)境軟件、硬件、網(wǎng)絡(luò)、測試數(shù)據(jù)、工具是否滿足測試要求環(huán)境檢查單、環(huán)境配置基線執(zhí)行測試按用例跑程序?qū)嶋H結(jié)果和預(yù)期是否一致缺陷記錄、測試執(zhí)行結(jié)果Bug 跟蹤提交的缺陷是否修復(fù)修復(fù)有沒有引入新問題回歸結(jié)果、Bug 關(guān)閉記錄測試報告當(dāng)前版本質(zhì)量是否達到上線標(biāo)準(zhǔn)測試報告、缺陷分析這個表格里的順序不能亂尤其是需求評審和需求分析很多人會把它們合并成“看一下需求”但評審的核心是“找問題”分析的核心是“定范圍”。當(dāng)時在運輸網(wǎng)站這個項目上需求評審階段就發(fā)現(xiàn)一處業(yè)務(wù)規(guī)則描述前后矛盾——用戶修改密碼后舊 token 是否立即失效文檔里寫了兩種結(jié)論。如果這個矛盾沒有在評審階段暴露后面用例設(shè)計、執(zhí)行、回歸全部都會跟著返工。每個階段把上階段的產(chǎn)出當(dāng)作輸入問題越早發(fā)現(xiàn)返工成本越低。2.2 測試方法選型白盒、黑盒、灰盒與階段劃分怎么對應(yīng)實踐測試方法從不同視角有不同的分類方式但落到日常測試開發(fā)工作里選型邏輯比記憶分類本身更重要。從是否關(guān)心軟件內(nèi)部結(jié)構(gòu)來看白盒測試要讀代碼、分析邏輯路徑一般由開發(fā)人員自己做的單元測試覆蓋黑盒測試完全不看內(nèi)部實現(xiàn)只驗證輸入輸出是否符合需求是功能測試和系統(tǒng)測試最常用的方式灰盒測試介于兩者之間關(guān)注接口報文、數(shù)據(jù)流不關(guān)心內(nèi)部具體實現(xiàn)——接口自動化測試基本都屬于這一類。從是否執(zhí)行代碼的角度靜態(tài)測試靠代碼評審和靜態(tài)掃描工具做動態(tài)測試靠真正運行程序做。按開發(fā)階段劃分的單元、集成、確認(rèn)、系統(tǒng)、驗收、回歸這六類日常工作里接觸最多的是系統(tǒng)測試和回歸測試。在運輸網(wǎng)站這個項目里主營業(yè)務(wù)的登錄、訂單、運單查詢走的是黑盒功能測試接口自動化接近灰盒視角安全相關(guān)專項測試則要結(jié)合系統(tǒng)配置、權(quán)限矩陣做系統(tǒng)級驗證。選型的關(guān)鍵不是哪種方法更高級而是被測對象處于什么階段、缺陷最容易藏在哪一層。一般情況下業(yè)務(wù)功能層面的缺陷用黑盒用例就能暴露接口層的字段校驗和異常處理要靠灰盒接口用例兜底涉及性能和安全的部分才需要專門的壓測與安全測試介入。2.3 用例設(shè)計實操從需求拆功能點到邊界數(shù)據(jù)用例設(shè)計是測試執(zhí)行前最重要的手工活。當(dāng)時在主管指導(dǎo)下做登錄模塊用例時用的是一套很樸素的步驟后來發(fā)現(xiàn)這套步驟放到任何業(yè)務(wù)模塊都通用。第一從需求文檔里提取功能點。登錄模塊的功能點是正確用戶名和密碼可以登錄、錯誤密碼拒絕登錄、不存在用戶拒絕登錄、空用戶名和空密碼提示必填。第二按功能點拆正常場景和異常場景。正常場景走通主流程異常場景覆蓋提示信息是否準(zhǔn)確、系統(tǒng)是否報錯而不是崩潰。第三補充邊界和權(quán)限場景。比如密碼長度上下限、用戶名是否區(qū)分大小寫、連續(xù)登錄失敗后是否有鎖定策略。第四把環(huán)境相關(guān)的前提寫進前置條件。比如本地數(shù)據(jù)庫是否存在對應(yīng)測試賬號、是否需要先清空登錄鎖定記錄。這套步驟的關(guān)鍵在于“先列功能點再設(shè)計數(shù)據(jù)”而不是拿著需求文本直接寫用例。功能點列的越全測試數(shù)據(jù)的針對性越強。用例設(shè)計完一定要評審評審時開發(fā)會從實現(xiàn)角度指出一些不可能出現(xiàn)的輸入產(chǎn)品會從業(yè)務(wù)角度補充一些真實用戶會做的操作兩輪評審下來遺漏率會明顯下降。2.4 一份定位不靠問的 Bug 單要素與判斷標(biāo)準(zhǔn)實習(xí)期間最受用的一句話是優(yōu)秀的 Bug 單開發(fā)不用跑來問測試就能知道怎么重現(xiàn)。判斷一個 Bug 單是否合格不是看描述寫了多少字而是看開發(fā)拿到單子之后能否不依賴任何口述獨立復(fù)現(xiàn)問題并理解影響范圍。一份完整的 Bug 單至少要覆蓋 12 類信息所屬系統(tǒng)、發(fā)現(xiàn)的版本、所屬模塊、提交人、錯誤類型、重現(xiàn)概率、嚴(yán)重級別、優(yōu)先級、標(biāo)題、Bug 單號、Bug 內(nèi)容、附件。其中最容易敷衍的是標(biāo)題和重現(xiàn)步驟常見錯誤是直接把測試用例的名字復(fù)制一遍比如“登錄模塊測試001失敗”這種標(biāo)題對定位沒有任何幫助。合格的標(biāo)題應(yīng)該一句話說清“什么功能在什么條件下出了什么問題”例如“修改密碼后舊 token 仍可訪問訂單接口”。下面是一個可以直接套用的示例字段示例內(nèi)容所屬系統(tǒng)運輸網(wǎng)站-客戶管理模塊發(fā)現(xiàn)的版本V1.2.3所屬模塊登錄認(rèn)證錯誤類型代碼錯誤重現(xiàn)概率必現(xiàn)嚴(yán)重級別嚴(yán)重優(yōu)先級高標(biāo)題修改密碼后舊 token 仍可請求訂單接口重現(xiàn)步驟1. 登錄獲取 token 2. 修改密碼 3. 攜帶舊 token 請求訂單列表預(yù)期結(jié)果返回 401要求重新登錄實際結(jié)果返回 200 和訂單列表數(shù)據(jù)附件舊 token 請求日志、接口響應(yīng)截圖提交 Bug 時盡量附上截圖并做標(biāo)注操作過程說明清楚。字不如圖描述半天的文本信息量可能不如一張帶紅框標(biāo)注的截圖。重現(xiàn)概率、嚴(yán)重級別、優(yōu)先級三個字段要分開填重現(xiàn)概率描述穩(wěn)定性嚴(yán)重級別描述對用戶的影響程度優(yōu)先級描述修復(fù)的緊迫性。比如一個只在特定瀏覽器出現(xiàn)的頁面布局錯位嚴(yán)重級別是“一般”優(yōu)先級可以是“低”但一個會導(dǎo)致用戶訂單數(shù)據(jù)錯亂的場景即使出現(xiàn)概率是“小概率”嚴(yán)重級別也必須是“嚴(yán)重”優(yōu)先級要提到“高”。填這三個字段時想的不是“這個 bug 多嚴(yán)重”而是“如果我是項目負(fù)責(zé)人看到這張單子會先安排誰處理”。3. TCP 連接壓力測試的三層限制ulimit、端口范圍與 TIME_WAIT 內(nèi)核參數(shù)3.1 壓測目標(biāo)與初始現(xiàn)象默認(rèn)只能建立 1014 個連接壓力測試是進入公司后獨立完成的第一個完整測試任務(wù)。被測系統(tǒng)是基于 TCP/IP 的 C/S 架構(gòu)服務(wù)端監(jiān)聽固定端口客戶端通過循環(huán)不斷發(fā)起連接請求目標(biāo)是驗證服務(wù)器最多能響應(yīng)多少個并發(fā)客戶端連接。每成功建立一個客戶端連接服務(wù)端就維護一個連接計數(shù)。用客戶端腳本把目標(biāo)并發(fā)數(shù)開到 10 萬后連接數(shù)到 1000 出頭就停住了。用ulimit -n查看發(fā)現(xiàn)當(dāng)前用戶最多同時打開 1024 個文件。Linux 下一切皆文件socket 也是文件描述符的一種每建立一個 TCP 連接就要消耗一個文件描述符1024 個文件描述符扣掉標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯誤、服務(wù)端監(jiān)聽 socket 等固有占用可用的連接名額只剩 1014 個。這就是壓測上不去的第一層瓶頸。3.2 第一層限制文件描述符與 limits.conf文件描述符限制分軟限制和硬限制兩層。軟限制是當(dāng)前會話內(nèi)可以隨時調(diào)低或調(diào)高的值硬限制是軟限制可以到達的上限。直接執(zhí)行ulimit -n 100000會失敗因為修改后的數(shù)值超過了硬限制這是 Linux 防止普通用戶無限消耗系統(tǒng)資源的一道閘門。正確的修改方式是編輯/etc/security/limits.conf給指定用戶配置 nofile 的軟硬限制# 編輯 /etc/security/limits.conf追加以下兩行 # 第一列是用戶名第二列是限制類型第三列是資源項目第四列是數(shù)值 speng soft nofile 100000 speng hard nofile 100000軟限制和硬限制要一起放開只調(diào) soft 不調(diào) hard后續(xù)進程運行中如果嘗試再次提升軟限制仍然會被硬限制擋住。修改后需要重新登錄會話同時要確認(rèn)/etc/pam.d/login里有session required pam_limits.so這一行否則登錄會話不會去讀取 limits.conf 的配置。這個文件決定的是用戶在登錄階段能不能獲得配置的限額很多人改完 limits.conf 發(fā)現(xiàn)ulimit -n沒變就是漏了 pam 模塊。3.3 第二層限制fs.file-max 與本地端口范圍文件描述符放開之后繼續(xù)壓測仍然遠(yuǎn)遠(yuǎn)達不到 10 萬而且結(jié)果不穩(wěn)定。此時用cat /proc/sys/fs/file-max查看系統(tǒng)級最大文件數(shù)發(fā)現(xiàn)數(shù)值只有一萬多。這個值是根據(jù)內(nèi)核參數(shù)和硬件資源計算出來的系統(tǒng)級硬上限是所有進程加起來的文件描述符總量不是單個用戶的限制。它受內(nèi)存影響同一個環(huán)境下不同機器的值可能差很多不能用機器 A 的數(shù)值去倒推機器 B 的容量。繼續(xù)往上走還會遇到端口范圍限制。TCP 連接的四元組是“源 IP、源端口、目標(biāo) IP、目標(biāo)端口”壓測客戶端作為主動連接方每建立一個連接都要占用一個本地端口。端口號只有 16 位最大只能是 65535Linux 默認(rèn)的本地端口范圍通常是 32768 到 60999可用端口數(shù)有限連接數(shù)自然上不去。修改/etc/sysctl.conf擴大本地端口范圍# 編輯 /etc/sysctl.conf追加 net.ipv4.ip_local_port_range 1024 65535 # 執(zhí)行 sysctl -p 使配置生效1024 以下為特權(quán)端口一般不改動65535 是 16 位端口號的上限所以最大只能配置到這個值。修改后理論上能承擔(dān)的并發(fā)連接從兩三萬擴展到六萬多但實測仍然達不到 10 萬而且結(jié)果時高時低。到這里已經(jīng)能明確判斷問題不在連接數(shù)量的初始配額而在連接的生命周期管理上。3.4 第三層限制TIME_WAIT 堆積與內(nèi)核參數(shù)用 netstat 統(tǒng)計當(dāng)前 TCP/IP 連接狀態(tài)分布命令是netstat -an | awk /^tcp/{s[$NF]} END {for(a in s) print a, s[a]}輸出里 TIME_WAIT 數(shù)量非常多。TIME_WAIT 是主動關(guān)閉連接的一方在收到對端 FIN 后進入的狀態(tài)默認(rèn)要等待 2 個 MSL最大報文段生存時間通常是 60 秒左右。壓測場景下客戶端每建一個連接就斷開大量連接短時間進入 TIME_WAIT舊的連接還沒釋放完新的連接已經(jīng)占不到端口和連接表項了。這才是連接數(shù)上不去的根本原因。在 /etc/sysctl.conf 里追加一組連接回收相關(guān)的內(nèi)核參數(shù)# 開啟 TIME_WAIT socket 重用允許將其用于新的 TCP 連接 net.ipv4.tcp_tw_reuse 1 # 開啟 TIME_WAIT socket 快速回收內(nèi)核 4.12 后已移除新環(huán)境勿用 net.ipv4.tcp_tw_recycle 0 # 縮短 TIME_WAIT 等待時間這里設(shè)置為 5 秒 net.ipv4.tcp_fin_timeout 5 # 加大 SYN 隊列長度容納更多等待連接的請求 net.ipv4.tcp_max_syn_backlog 8192這里有一個容易踩的坑原文資料里寫的net.ipv4.tcp_tw_fin_timewait參數(shù)名是錯的內(nèi)核里對應(yīng)的是net.ipv4.tcp_fin_timeout直接照抄舊參數(shù)名sysctl -p會報“沒有這個鍵”。另一個坑是tcp_tw_recycle它在 Linux 4.12 版本已經(jīng)被移除而且它在 NAT 環(huán)境下開啟會導(dǎo)致隨機丟包讓部分用戶連接異?!,F(xiàn)在的新環(huán)境不要開 recycle用tcp_tw_reuse配合縮短tcp_fin_timeout就夠用。當(dāng)年在實習(xí)環(huán)境里開啟后確實大幅回收了 TIME_WAIT但那是舊內(nèi)核下的行為換成生產(chǎn)環(huán)境不能照搬。調(diào)優(yōu)之后重新執(zhí)行壓測連接數(shù)從一萬多穩(wěn)步上升到 9 萬多接近預(yù)設(shè)的 10 萬目標(biāo)。整個鏈路整理下來是這樣一個遞進關(guān)系限制層級關(guān)鍵配置生效方式常見坑文件描述符ulimit -n / limits.conf重新登錄、pam_limits.so只改 soft 不改 hard或漏掉 pam 模塊系統(tǒng)文件數(shù)fs.file-max內(nèi)核自動計算系統(tǒng)級上限受硬件資源影響端口范圍ip_local_port_rangesysctl -p16 位上限最大只能 65535連接回收tcp_tw_reuse / tcp_fin_timeoutsysctl -ptw fin 參數(shù)名易寫錯recycle 新內(nèi)核已移除3.5 客戶端壓測腳本循環(huán)建連與并發(fā)控制調(diào)優(yōu)過程中用到的壓測腳本并不復(fù)雜核心就是循環(huán)建連加并發(fā)控制。用 Python 的 socket 和 threading 就可以完成# load_test.py import socket import threading import time target_ip 192.168.1.100 # 被測服務(wù)端地址 target_port 8080 # 被測服務(wù)端端口 target_count 100000 # 目標(biāo)連接總數(shù) success 0 # 成功連接計數(shù) lock threading.Lock() # 線程安全計數(shù)器 def connect_once(): global success try: s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(3) s.connect((target_ip, target_port)) with lock: success 1 time.sleep(600) # 保持連接讓服務(wù)端維持并發(fā)數(shù)量 except Exception as e: print(str(e)[:80]) # 只打印錯誤片段避免刷屏 threads [] for i in range(target_count): t threading.Thread(targetconnect_once) t.start() threads.append(t) time.sleep(0.001) # 減緩建連速率避免客戶端句柄瞬間耗盡 for t in threads: t.join() print(success:, success)connect_once里每個線程建立一個 socket 連接成功后保持連接不釋放這樣服務(wù)端的并發(fā)連接數(shù)才會持續(xù)累計。time.sleep(0.001)控制建連速率毫秒級間隔可以讓連接請求更接近生產(chǎn)環(huán)境的真實到達模式避免瞬間打滿客戶端自身資源導(dǎo)致誤判。計數(shù)用 lock 保護因為多線程下對 success 的并發(fā)寫會導(dǎo)致計數(shù)丟失。注意客戶端自身也有文件描述符上限如果目標(biāo)是 10 萬連接單機客戶端不一定能扛住常見做法是拆成多臺壓測機分布式執(zhí)行或者用協(xié)程替代線程降低客戶端資源占用。4. pytest 自動化測試實現(xiàn)fixture 前置處理與 parametrize 參數(shù)化登錄用例4.1 為什么是 pytestfixture、參數(shù)化和斷言機制壓力測試解決的是“服務(wù)器能扛多少”的問題自動化測試解決的是“每次改版后這些功能還能不能跑通”的問題。公司自動化測試選了 pytest 框架原因集中在三點fixture 可以復(fù)用測試前置邏輯parametrize 可以天然生成用例組合斷言直接使用 Python 原生 assert不需要額外學(xué)習(xí)一套斷言 API。比起 unittest 的類繼承結(jié)構(gòu)pytest 函數(shù)式的組織方式對測試開發(fā)更友好寫起來也短很多。4.2 fixture執(zhí)行前的準(zhǔn)備和執(zhí)行后的清理fixture 用來處理“執(zhí)行測試前需要做的事”和“執(zhí)行測試后需要收尾的事”。登錄測試場景里每個用例執(zhí)行前需要清理掉上次登錄失敗留下的鎖定記錄用例執(zhí)行后需要把環(huán)境復(fù)位避免影響下一個用例。這兩件事在 pytest 里用一個 fixture 就能完成# test_login.py 中的 fixture 定義 import pytest from login import login # 被測函數(shù)實際項目中對應(yīng)登錄接口封裝 pytest.fixture def clean_login_lock(): # 用例執(zhí)行前清空登錄失敗鎖定記錄 print(clean login lock records) yield # yield 之前是前置yield 之后是清理 # 用例執(zhí)行后復(fù)位環(huán)境 print(reset environment)yield 之前的代碼在用例執(zhí)行前運行yield 之后的代碼在用例結(jié)束后運行不管用例通過還是失敗清理代碼都會執(zhí)行。這一點比在用例里手動寫 try/finally 干凈很多。如果多個測試文件都需要這個 fixture就把它放到 conftest.py 里pytest 會自動發(fā)現(xiàn)不需要顯式 import。4.3 parametrize 參數(shù)化登錄用例組合與 ids 可讀化登錄測試的特點是同一個入口、多組輸入數(shù)據(jù)。用 mark.parametrize 把用例數(shù)據(jù)抽出來一組參數(shù)就是一條用例不需要為每個輸入組合寫一個測試函數(shù)。參數(shù)化配合 ids 可以讓每條用例在測試報告中顯示為可讀的名稱方便定位是哪組數(shù)據(jù)掛在哪個用例上# test_login.py pytest.mark.parametrize( username, password, expected_code, [ (admin, admin123, 0), # 正確憑證預(yù)期成功 (admin, wrong, 1), # 密碼錯誤預(yù)期拒絕 (guest, guest123, 1), # 無權(quán)限用戶預(yù)期拒絕 (, , 1), # 空用戶名和密碼預(yù)期提示必填 ], ids[correct, wrong-password, no-permission, empty-fields] ) def test_login(clean_login_lock, username, password, expected_code): result login(username, password) assert result[code] expected_codeparametrize 第一個參數(shù)是參數(shù)字段名第二個參數(shù)是數(shù)據(jù)列表ids 負(fù)責(zé)給每個組合起名。執(zhí)行時 pytest 會把 username、password、expected_code 三組值注入到測試函數(shù)里每組數(shù)據(jù)生成一條獨立用例。fixture 參數(shù)放在 parametrize 參數(shù)之前pytest 會自動先執(zhí)行 fixture 再執(zhí)行用例。實際項目中 login 函數(shù)通常替換為 requests.post 調(diào)登錄接口返回值從字典變成了接口響應(yīng)但測試結(jié)構(gòu)完全一致。4.4 運行與結(jié)果解讀在項目目錄下執(zhí)行pytest test_login.py -v-v 參數(shù)輸出每條用例的詳細(xì)執(zhí)行結(jié)果可以看到類似下面的輸出test_login.py::test_login[correct] PASSED test_login.py::test_login[wrong-password] PASSED test_login.py::test_login[no-permission] PASSED test_login.py::test_login[empty-fields] PASSED方括號里的內(nèi)容就是 ids 設(shè)置的名字。如果某條用例失敗pytest 會把 assert 前后的實際值和預(yù)期值直接對比展示出來不用加額外的日志就能看到是哪里不匹配。這里有個細(xì)節(jié)parametrize 里如果數(shù)據(jù)量很大比如要跑幾十組賬號密碼組合ids 不寫也可以pytest 會自動用參數(shù)值做標(biāo)識但參數(shù)值包含特殊字符時會很難讀所以 ids 建議在用例矩陣固定后統(tǒng)一補充。5. 壓測與回歸的工程化技巧環(huán)境體檢腳本、JVM 內(nèi)存防 OOM 與 Jenkins 接入5.1 壓測前環(huán)境體檢腳本壓測調(diào)優(yōu)踩過的坑第二次就不該用試錯方式再走一遍。把三類限制和連接狀態(tài)統(tǒng)計寫成一個體檢腳本壓測前先跑一遍確認(rèn)每一項都處于放開狀態(tài)效率會比邊壓測邊查高很多#!/bin/bash # 壓測前體檢逐項打印資源限制和連接狀態(tài) echo 當(dāng)前進程文件描述符上限 ulimit -n echo 系統(tǒng)級文件描述符上限 cat /proc/sys/fs/file-max echo 本地端口范圍 cat /proc/sys/net/ipv4/ip_local_port_range echo 連接回收相關(guān)內(nèi)核參數(shù) sysctl net.ipv4.tcp_tw_reuse net.ipv4.tcp_fin_timeout net.ipv4.tcp_max_syn_backlog 2/dev/null echo 目標(biāo)端口連接狀態(tài)分布 netstat -an | grep :8080 | awk {print $6} | sort | uniq -c最后一行統(tǒng)計目標(biāo)端口當(dāng)前 TCP 狀態(tài)分布用sort | uniq -c做分組計數(shù)可以直接看到 ESTABLISHED 和 TIME_WAIT 的數(shù)量級。判斷邏輯很簡單TIME_WAIT 數(shù)量持續(xù)增長說明回收參數(shù)沒生效ulimit 值低于預(yù)期說明 limits.conf 或 pam 配置不對端口范圍最大值小于 65535說明 sysctl.conf 沒有加載成功。5.2 壓測客戶端的 JVM 內(nèi)存配置與 OOM壓測過程中服務(wù)端會不斷優(yōu)化但客戶端自身也會成為瓶頸。如果壓測工具是基于 JVM 的比如 Gatling 或自研的 Java 壓測客戶端高并發(fā)下最容易出現(xiàn)的是堆內(nèi)存溢出和頻繁 Full GC。每個 socket 連接在 JVM 內(nèi)部對應(yīng)著對象和緩沖區(qū)連接數(shù)上來之后堆內(nèi)存占用會快速膨脹。開發(fā)測試階段就合理設(shè)置 JVM 運行內(nèi)存能提前避免這類 OOM 問題java -Xms2g -Xmx4g -XX:UseG1GC -XX:MaxDirectMemorySize1g -jar load-client.jar-Xms 和 -Xmx 控制堆內(nèi)存的初始值和最大值壓測工具建議把兩者設(shè)置為相同值避免運行中動態(tài)擴容引發(fā)停頓。-XX:MaxDirectMemorySize 限制堆外直接內(nèi)存NIO 場景下 socket 讀寫緩沖走的是直接內(nèi)存這個值太小同樣會拋 OOM。還要注意給操作系統(tǒng)預(yù)留余量堆內(nèi)存加直接內(nèi)存不要超過物理內(nèi)存的 70%否則內(nèi)存換出會直接拉高壓測延遲測出來的數(shù)據(jù)失真。5.3 把 pytest 用例接進 Jenkins 做發(fā)版回歸壓測確認(rèn)容量自動化測試守住功能兩者最終要在持續(xù)集成里匯合。Jenkins 的構(gòu)建任務(wù)里加一個執(zhí)行 shell 的構(gòu)建步驟跑pytest test_login.py -v --junitxmlreport.xmlpytest 會把結(jié)果輸出成 JUnit 格式的 XMLJenkins 再配一個 JUnit 插件把 XML 解析成趨勢圖每次發(fā)版前自動跑一遍登錄用例。用例少的時候這個流程看不出優(yōu)勢但用例集到幾百條之后手動回歸一次要一兩個小時Jenkins 自動跑只需要幾分鐘而且每次代碼提交都能觸發(fā)不用等人想起來才執(zhí)行。本文還有配套的精品資源點擊獲取