iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0
Software Development

不會寫程式,我照樣做效能測試 - Claude Code × k6 的 30 天系列 第 28 篇

Day 28|WebSocket 與其他協定:不只是 HTTP

  • 分享至 

  • xImage
  •  

一、當請求不再是「一來一回」

前面二十七天的所有測試,骨子裡都是同一個動作:發一個請求,等一個回應,量中間的時間。HTTP 的世界觀就是一來一回,http_req_duration 量的就是這一回合。但有些功能不是這樣運作的:聊天室的訊息是伺服器「推」給你的,你沒有發請求;即時報價的數字自己在跳;線上遊戲、共編文件、IoT 儀表板——這些的底層是 WebSocket:連線建立一次,之後雙方隨時互丟訊息,連線一直掛著。

這個差別讓效能測試的問題整組換掉。HTTP 測試問「一次請求多久回來」「每秒能處理幾個請求」;長連線的世界裡,連線掛著不動也占資源,於是問題變成:「同時掛一萬條連線,伺服器撐得住嗎」「訊息從伺服器推出來,多久到我手上」「掛了一小時的連線,會不會被誰默默斷掉」。一句話:HTTP 量回合,WebSocket 量「同時掛著的量」與「訊息的延遲」。

https://ithelp.ithome.com.tw/upload/images/20260923/20161809gQZUcpgVcq.png
圖 1:一來一回 vs 一直掛著——兩種世界觀,兩組完全不同的指標

對應到 k6 的指標,變化是這樣:
https://ithelp.ithome.com.tw/upload/images/20260923/20161809RD4fBK2kb9.png

表裡最重要的一格是右上角:訊息延遲 k6 不會自動給你。HTTP 的一來一回,k6 兩頭都看得到,時間自己就量出來了;伺服器主動推的訊息,k6 只看得到「收到的那一刻」,不知道它是什麼時候出發的。解法很老派也很有效:讓訊息裡帶著出發時間,收到時相減。這件事第三節動手做。

二、思維轉換:VU 在長連線測試裡是什麼

寫腳本之前,先把一個觀念換過來,不換的話數字會全部讀錯。

HTTP 測試裡,VU 是「不斷發請求的人」,30 個 VU 可以每秒打出上百個請求;長連線測試裡,VU 是「掛著的一條連線」——它連上去、待著、偶爾收發訊息。所以 30 個 VU 的意思不再是「一秒上百請求的壓力」,而是「同時 30 條連線」。要測一千條同時連線,就真的需要一千個 VU——好消息是掛著的連線很便宜,k6 撐幾千條協定層 WebSocket 連線沒有問題;真的要上萬,回 Day 25 的三條路。

負載模型也跟著換。Day 12 的 ramping 在這裡的意義是「連線湧入的速度」——尖峰時每秒新建幾條連線,本身就是一個要測的東西(連線建立比維持貴得多);Day 24 的 soak 在長連線世界甚至更重要,因為長連線最常見的事故就是「掛久了出事」:伺服器、負載平衡器、防火牆都可能有自己的閒置逾時,60 秒沒動靜就把連線默默剪掉——使用者的體感是「聊天室用著用著就斷線重連」。測長連線,時間軸永遠是主角之一。

三、動手做:壓 QuickPizza 的 WebSocket

QuickPizza 有一個 WebSocket 端點可以練習。讓 Claude Code 生成,需求裡把長連線的關鍵都點名:

Prompt 1|WebSocket 壓測腳本

請幫我寫一支 k6 WebSocket 壓測腳本(tests/ws.js),目標是 QuickPizza 的
WebSocket 端點(請先查官方文件或範例確認目前的端點路徑與訊息格式):
- 50 個 VU,每個 VU:建立連線 → 掛著 3 分鐘 → 期間每 10 秒送一則訊息
  (訊息內容帶上送出當下的時間戳)→ 收到訊息時,若含時間戳就計算延遲
- 用自訂 Trend 指標記錄「訊息延遲」,叫 msg_latency
- thresholds:連線建立 ws_connecting p(95)<1000;msg_latency p(95)<500;
  連線失敗率要能看到
- 請用 k6 目前建議的 WebSocket 模組寫法(新舊兩套 API 請幫我選現行建議的那套)
- 逐段解釋,特別是:時間戳夾帶與相減的位置、VU 掛著時程式在「等」什麼

骨架大致如下(模組寫法與端點以 Claude Code 查到的現行版本為準):

import { WebSocket } from 'k6/experimental/websockets';   // 以現行建議模組為準
import { Trend, Rate } from 'k6/metrics';

const msgLatency  = new Trend('msg_latency', true);
const connectFail = new Rate('ws_connect_fail');

export const options = {
  vus: 50, duration: '3m',
  thresholds: {
    ws_connecting:  ['p(95)<1000'],
    msg_latency:    ['p(95)<500'],
    ws_connect_fail:['rate<0.01'],
  },
};

export default function () {
  const ws = new WebSocket('wss://quickpizza.grafana.com/ws');  // 端點以查到的為準
  ws.onopen = () => {
    // 每 10 秒送一則帶時間戳的訊息
    const timer = setInterval(() => {
      ws.send(JSON.stringify({ t: Date.now(), msg: 'ping from k6' }));
    }, 10000);
    setTimeout(() => { clearInterval(timer); ws.close(); }, 170000); // 掛滿再收
  };
  ws.onmessage = (e) => {
    try {
      const d = JSON.parse(e.data);
      if (d.t) msgLatency.add(Date.now() - d.t);   // 出發時間相減=延遲
    } catch (_) {}
  };
  ws.onerror = () => connectFail.add(1);
}

跑起來之後,讀數的方式照這個順序:

先看連線層。ws_connecting 的 p95 是「建立一條連線要多久」——50 條同時湧入時它會不會飆?connect_fail 有沒有出現?這一層有問題,後面都不用看。再看 session。ws_session_duration 應該貼著你設計的時長(掛 170 秒就該接近 170 秒)——如果一堆 session 只活了 60 秒上下,恭喜你抓到了某一層的閒置逾時,這是長連線測試最有價值的發現之一,把「60 秒」這個數字帶去 Day 22 開單,RD 會知道去查哪裡。最後看訊息。msg_latency 的 p95 和分佈——它是長連線世界的 http_req_duration,Day 3 的百分位、Day 18 的分佈思維全部適用。

判讀交給 Claude Code 的 prompt 邏輯照舊(Day 18、24 的路數),多教它一件事:session 長度的分佈比平均重要——平均 150 秒可能是「大多數活滿、少數秒斷」,也可能是「全部活 150 秒被齊頭剪掉」,兩者是完全不同的問題。

四、SOAP:老朋友,新工具,同一招

換個完全不同的方向。如果你在台灣的金融、保險、電信或政府環境做測試,大概率遇過 SOAP——WSDL、信封(Envelope)、XML 巢狀到天邊。這些系統穩定運行了十幾二十年,也還會繼續跑很多年,「效能測試工具都在講 REST 和 JSON,我的系統是 SOAP 怎麼辦」是個真實的焦慮。

答案短得出乎意料:對 k6 來說,SOAP 就是「POST 一段 XML 到一個網址」。它跑在 HTTP 上,所以 Day 8 的 http.post、Day 3 的百分位、Day 7 的 thresholds、Day 12 的負載模型——全部直接適用,一天新東西都不用學。差別只有三個細節:Content-Type 是 XML 的、要多一個 SOAPAction 標頭、body 是那段信封。實際長相:

import http from 'k6/http';
import { check } from 'k6';

const envelope = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <QueryBalance xmlns="http://example.com/bank">
      <AccountId>${__ENV.TEST_ACCOUNT}</AccountId>
    </QueryBalance>
  </soap:Body>
</soap:Envelope>`;

export default function () {
  const res = http.post('https://staging.example.com/BankService.asmx', envelope, {
    headers: {
      'Content-Type': 'text/xml; charset=utf-8',
      'SOAPAction':  '"http://example.com/bank/QueryBalance"',
    },
  });
  check(res, {
    '狀態 200': (r) => r.status === 200,
    '沒有 SOAP Fault': (r) => !r.body.includes('<soap:Fault>'),   // 重要!見下
  });
}

兩個 SOAP 特有的坑。第一,錯誤藏在 200 裡。SOAP 服務常常在出錯時照樣回 HTTP 200,錯誤寫在 body 的 Fault 元素裡——只 check 狀態碼,你會拿到一份全綠但全錯的報告(Day 11 除錯八成在腳本的老教訓,SOAP 版)。所以 check 一定要看 body。第二,信封不用手寫。跟 RD 要 WSDL 檔或一段實際請求的範例(用系統既有的工具或紀錄都抓得到),丟給 Claude Code:「照這個 WSDL/範例幫我產生 QueryBalance 的請求信封,帳號參數化」——XML 巢狀到天邊沒關係,那是它的工作。你的工作還是 Day 4 的五要素和 Day 16 的門檻。

五、gRPC:知道存在,需要時再深入

最後一個名詞用一頁講完。gRPC 是微服務之間常用的通訊協定——服務對服務的內部呼叫,追求高效,用二進位格式(Protocol Buffers)而不是 JSON。你在瀏覽器的網路面板看不到它,因為它多半活在機房內部,服務與服務之間。

關於它,QA 需要知道的就三件事。第一,k6 原生支援(k6/net/grpc 模組),寫起來和 http 模組的手感類似:連線、呼叫方法、check 回應。第二,需要 proto 檔——gRPC 的介面定義寫在 .proto 檔裡,相當於 SOAP 的 WSDL;沒有它腳本寫不了,而它在 RD 的 repo 裡,所以測 gRPC 天生就是協作題。第三,測試的發起點通常在內網——gRPC 服務很少直接對外,你的施壓位置要能碰到它,Day 25 借內網機器的做法在這裡是常態。

什麼時候需要深入?當你的團隊說「前台 API 很快,慢的是後面服務之間的呼叫」——那句話出現時,回來把 gRPC 撿起來,向 RD 要 proto 檔,讓 Claude Code 帶你寫第一支。在那之前,知道 k6 測得了它、知道要跟誰要什麼,就夠了。

六、注意事項:跨出 HTTP 的六個習慣

https://ithelp.ithome.com.tw/upload/images/20260923/201618092eZ4AQMZIK.png

給 RD 的一句話:QA 拿著「session 大量集中在 60 秒左右結束」來找你時,那幾乎不是巧合——查一下 LB、反向代理、防火牆的 idle timeout,通常一小時內就能定位。SOAP 那邊他最需要的是 WSDL 或一段實際請求範例;gRPC 則是 proto 檔和一個碰得到服務的施壓位置。這三樣都是你舉手之勞、他自己生不出來的東西。

七、觀念驗證:三個問題確認你有帶走今天的重點

• HTTP 測試與長連線測試各自問什麼問題?VU 在兩種測試裡分別代表什麼?(第一、二節)
• 訊息延遲為什麼要自己夾時間戳?session 長度為什麼看分佈不看平均——齊頭的 60 秒暗示什麼?(第三節)
• 「k6 測 SOAP 就是 POST XML」這句話為什麼成立?SOAP 最容易讓報告全綠但全錯的坑是什麼?(第四節)

八、小結

跨出 HTTP,變的是世界觀,不變的是方法。WebSocket 把問題從「一次多久」換成「掛著多少、訊息多快、掛久了會不會被剪」——VU 變成連線本身,延遲要自己夾時間戳,齊頭的 session 壽命是最值錢的線索;SOAP 對 k6 而言就是 POST XML,台灣的金融保險政府環境用得再多也不用怕,只記得錯誤藏在 200 的 body 裡;gRPC 知道三件事就好——k6 支援、要 proto 檔、天生是協作題。而百分位、thresholds、負載模型、soak、開單——前面二十七天的每一課,換了協定一樣用。工具的部分到此全部走完。明天退一步看這三十天裡一直站在你旁邊的那位:Claude Code 到底能做什麼、不能做什麼——包括這一路上它錯過哪些、人是怎麼接住的——Day 29 見。


上一篇
Day 27|混合場景:背景負載+少量真實瀏覽器
系列文
不會寫程式,我照樣做效能測試 - Claude Code × k6 的 30 天 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言