一 分開三種緩衝與測試單位
接收資料變慢時,先分清 OS socket receive buffer、應用 framing buffer 與待處理 queue。socket buffer 是核心暫存區,應用 framing buffer 用來保留尚未組成完整 frame 的 bytes,queue 保存已組成但尚未消費的工作。把三者混成一個「buffer 大小」會找錯瓶頸,也無法知道記憶體由誰使用。
速率要同時用 bytes/s 與 frame/s 表示。假設每 frame 100 bytes,輸入 100 KB/s 約等於每秒 1000 frames;若 consumer 80 KB/s,10 秒輸入 1000 KB、消費 800 KB,理想 steady 估算堆積 200 KB。這是十進位 KB 與無額外封包負擔的容量示例,實測要另加 framing、header、對齊與 burst。
max_frame 必須先定義,例如 4096 bytes;queue_capacity 以 bytes、frames 或工作數明確表示。若一個工作包含 20 個小 frame,單看 frame 數會低估工作物件與索引記憶體。socket buffer 變大也不會提高 consumer 的實際處理率,只會延後 backpressure 或 overflow 的時間。
| 層次 | 保存內容 | 測試指標 | 常見誤判 |
|---|---|---|---|
| socket buffer | 核心收到的bytes | 讀取/阻塞 | 當成應用queue |
| framing buffer | 未完成frame | buffer bytes/cursor | 當成遺失 |
| application queue | 已組frame/工作 | depth/oldest age | 只看socket bytes |
| consumer | 解析/處理結果 | bytes/s、frame/s | 忽略burst |
| memory limit | 上述總和 | peak/limit | 只調大buffer |
應用 framing buffer 的 occupancy 要以 byte 和 frame count 同時記錄,因為一個大 frame 可能占滿容量但只增加一個 frame。若 queue 以工作物件保存,還要估算每個物件的固定 overhead;200 KB payload 不等於只需 200 KB RAM。
socket buffer 的 OS 預設值與可調上限需在測試機讀取並記錄,不能從應用 queue 容量推測。調整前後都要做相同測試,並確認調大核心 buffer 沒有讓整機記憶體壓力或其他連線退化。
二 用 100 KB/s 與 80 KB/s 建立基準
先做沒有 burst 的基準測試:producer 固定 100 KB/s、consumer 固定 80 KB/s、持續 10 秒、初始 queue=0,理想應看到 input 約 1,000 KB、output 約 800 KB、queue 增加約 200 KB。測試報告要寫單位是十進位 KB=1000 bytes,並列出每次取樣的 bytes_received、frames_completed、queue_bytes 與 latency。
排空時間取決於恢復後的服務餘量。若輸入停止且 consumer 維持 80 KB/s,200 KB 約需 2.5 秒排空;這是假設沒有新輸入、沒有額外 overhead 與處理抖動。若仍以 100 KB/s 到達,consumer 80 KB/s 就不會排空,queue 只會繼續增長直到背壓或上限。
加入 burst 測試,例如前1秒輸入500 KB、後9秒每秒20 KB,共680 KB,十秒平均68 KB/s低於consumer的80 KB/s;第一秒仍會理想堆積420 KB,接著每秒淨減60 KB,約七秒排空。要分別記錄 steady_rate、peak_burst、burst_duration 與最大 queue,不能只用十秒平均判斷容量。
| 案例 | 輸入 | 消費 | 10秒結果/假設 |
|---|---|---|---|
| steady | 100KB/s | 80KB/s | 約堆200KB |
| 停止後排空 | 停止輸入 | 80KB/s | 200KB約2.5s |
| burst | 1秒500KB後降速 | 80KB/s | 看peak queue |
| 快消費 | 100KB/s | 120KB/s | 可逐步排空 |
| 超過上限 | 100KB/s | 80KB/s、容量150KB | 記overflow/backpressure |
steady 測試要先丟棄暖機與連線建立期間,再算穩定窗口;burst 測試則保留完整開始時間。輸入端的 sleep、批次寫入或作業系統排程可能讓實際 burst 遠高於標稱 100 KB/s,需以接收端計數反推真實 arrival。
測試結束要保存 input、output、queue、overflow 與 latency 總表,讓容量結論可重算。
若 frame 有 12-byte header,1000 frames/s 的應用 payload 100 bytes 會產生額外 12 KB/s;若還有加密、封包或物件配置成本,實際 bytes/s 更高。測試資料需明列 payload 與 wire/application overhead,避免用裸 payload 估算 production 記憶體。
三 overflow 延遲與資源上限
in-test overflow 計數要和遺失原因分開:socket read error、framing reject、queue full、application drop、connection reset 各自計數。測試時可以故意把 queue_capacity 設為 150 KB 觀察 200 KB 堆積,但不要把這個壓力注入 production;壓測環境要隔離來源與設備。
每筆或每批要記錄 latency、queue_depth、oldest_age、memory_peak、CPU、frames_received、frames_completed。若 queue_bytes 沒超過上限但 latency 暴增,可能是 consumer 被鎖或解析慢;若 memory_peak 跟 framing buffer 一起升,可能是大 frame 或 cursor 未前移。
max_frame 與 queue 上限要互相檢查。一個合法 4096-byte frame 即使 queue 只剩 3000 bytes,也不能半存後標成完整;可拒絕新 frame、暫停讀取或使用持久化策略。選擇要依訊息是否可重建、是否允許背壓與業務重要性決定,不能預設丟資料。
| 故障 | 計數 | 先查 | 結果 |
|---|---|---|---|
| queue full | queue_full_count | queue bytes/oldest | 背壓或拒絕 |
| frame過大 | frame_reject_count | length/max_frame | 不allocate |
| 讀取錯誤 | socket_error_count | OS error/連線 | 分開記錄 |
| 處理落後 | latency/P99 | consumer/鎖 | 調整程式 |
| 記憶體超限 | memory_peak | 三層buffer | 停止壓測/降載 |
若測試中 queue full,記錄第一次滿的時間與恢復時間。丟棄策略若允許,只能丟明確可重建的查詢,並把 request_id 和 drop_reason 寫出;控制動作應停止新增、回報上游,而不是靜默刪除。
當 buffer 尚未滿但 latency 已超標時,不能只以「沒有 overflow」判定成功;業務驗收要同時有 queue oldest age、P99 latency 和完成率。若三者中任一超限,測試結果應標不合格並保留資源快照。
壓測結果只能支持被測配置。改變 OS socket buffer、worker 數、frame 大小或網路 offload 後要重新測;不能由一次 10 秒結果宣稱長時間穩定。資源上限與恢復行為都要寫進驗收條件。
四 背壓與排查限制
若 consumer 慢讀,TCP 可能透過接收窗口讓傳送端降低速度;這與應用 queue 主動丟棄不同。看到 producer 速率下降時,先比對 socket read 次數、核心 buffer、TCP window、應用 queue 與 consumer service time,再決定是正常 flow control 還是程式阻塞。
一次排查案例:10秒後實際received=998 KB、queue=198 KB、overflow=0、socket error=0、consumer=800 KB,符合 100/80 的理想差值;這不能說資料遺失。另一案例 queue=150 KB、overflow=50 KB、consumer=800 KB,表示達到自訂應用上限,需查 drop/backpressure policy。若 socket error 增加,則另查連線。
測試前先建立資源預算:socket buffer、framing buffer、queue、單筆最大 frame、工作物件與日誌暫存。設定 memory limit 後,在隔離環境逐步提高 burst;一旦 overflow 或 latency 超限就停止,保存結果。不要透過盲目放大 buffer 掩蓋 consumer 永久低於 arrival 的問題。
| 驗收項 | 基準 | 預期 |
|---|---|---|
| 穩定速率 | 100/80KB/s×10s | 約200KB queue |
| 排空 | 停止輸入、80KB/s | 約2.5s(理想) |
| 上限 | queue150KB | overflow/backpressure明確 |
| 大frame | 超max_frame | 拒絕且不allocate |
| 慢讀 | 降低read頻率 | 觀察TCP背壓非隨機丟失 |
延遲報告要區分 queue latency、framing latency 與 consumer latency。P99 變差但平均不變時,先查長尾 burst、鎖競爭與單一大 frame;不要只調整平均 buffer。
若 consumer 暫停 2 秒,短 burst 可能造成 queue 大幅上升;恢復後要量測排空時間與最老工作年齡。只看最終 queue 回到零,可能漏掉過程中延遲已超過業務上限。
本文是 socket 與應用佇列測試方法,不指定 Linux 或 Windows 的實際預設 buffer,也不提供 PLC 指令。OS 參數、socket API、核心統計與網路驅動行為需在目標平台查證。
五 驗收 FAQ 與來源
本案例 100 KB/s輸入、80 KB/s消費,基準期間10秒:輸入約 1,000 KB、消費約 800 KB,初始 queue=0 時理想堆積約 200 KB;以十進位 KB、無額外 overhead 為假設。實測要另記 burst、frame overhead、overflow、latency、memory 與資源上限。
TCP flow control 讓慢讀端可降低傳送端速度,通常表現為 backpressure,不代表核心隨機丟失應用資料。應用仍可能因 buffer 上限、程式主動丟棄、連線重置或 framing 錯誤而失資料;排查需把 socket 狀態、應用計數與連線事件放在同一時間線。
FAQ1:TCP 慢讀會自動隨機丟應用資料嗎?答:通常先表現為 flow control/backpressure;應用丟棄、queue full、reset 或 framing error 要分開計數。
FAQ2:socket buffer 加大能解決 consumer 80 KB/s 嗎?答:只能延後堆積或背壓,不能改變長期到達率高於服務率的事實。
FAQ3:為何要分 bytes/s 與 frames/s?答:同樣 bytes/s 下,frame 數會改變解析、索引與呼叫成本;兩者都要量測。
FAQ4:可以在正式設備直接做 overflow 壓測嗎?答:不應直接做。應在隔離測試環境,以明確資源上限與可恢復資料進行。
參考:RFC 9293:TCP reliable in-order byte-stream 與 flow control 背景,非應用佇列規格。
若同一資料同時有 frame_count 與 payload_bytes,驗收需核對兩者的比例與最大 frame。當平均速率正常但 frame_count 突升,可能是小包風暴,CPU與呼叫成本會先超限;當 payload_bytes 突升,可能是大包或來源異常。
參考:Linux socket(7) 官方手冊:socket buffer 與選項概念參考,需依目標OS核對。