WebSocket
Untuk aplikasi realtime: chat, notifikasi, dashboard live, trading. k6 menyediakan modul WebSocket berbasis event.
Sejumlah modul k6/experimental/* sudah lulus menjadi modul stabil. Skrip lama yang mengimpor k6/experimental/websockets dan k6/experimental/grpc perlu diperbarui menjadi k6/net/websockets dan k6/net/grpc. Modul k6/ws generasi pertama (berbasis callback, satu koneksi memblokir satu VU) sudah usang — gunakan modul berbasis event di bawah ini yang bisa menangani banyak koneksi paralel per VU.
import { WebSocket } from 'k6/net/websockets';
import { check, sleep } from 'k6';
import { Trend, Counter } from 'k6/metrics';
const latensiPesan = new Trend('latensi_pesan_ws', true);
const pesanDiterima = new Counter('pesan_ws_diterima');
export default function () {
const ws = new WebSocket('wss://echo.websocket.org');
const dikirimPada = {};
ws.onopen = function () {
// Kirim pesan berkala, seperti klien chat sungguhan
let n = 0;
const timer = setInterval(function () {
if (n++ >= 10) { clearInterval(timer); ws.close(); return; }
const id = 'msg-' + n;
dikirimPada[id] = Date.now();
ws.send(JSON.stringify({ id: id, teks: 'halo dunia' }));
}, 1000);
};
ws.onmessage = function (e) {
pesanDiterima.add(1);
try {
const data = JSON.parse(e.data);
if (dikirimPada[data.id]) {
latensiPesan.add(Date.now() - dikirimPada[data.id]);
}
} catch (err) { /* pesan bukan JSON, abaikan */ }
};
ws.onerror = function (e) { console.error('WS error: ' + e.error); };
}Berbeda dengan HTTP, satu VU WebSocket menahan satu koneksi TCP selama hidupnya. 10.000 VU berarti 10.000 koneksi terbuka bersamaan — Anda akan menabrak batas file descriptor OS (ulimit -n, default sering hanya 1024) jauh sebelum menabrak batas server.
Naikkan dengan ulimit -n 65535 di mesin generator sebelum menjalankan tes.
gRPC
import grpc from 'k6/net/grpc';
import { check } from 'k6';
const client = new grpc.Client();
// WAJIB di init context — pemuatan .proto tidak boleh di dalam default()
client.load(['definisi'], 'layanan.proto');
export default function () {
client.connect('grpc.contoh.id:9000', {
plaintext: false, // true bila tanpa TLS (hanya untuk lokal)
timeout: '10s',
});
const res = client.invoke('paket.LayananProduk/AmbilProduk', { id: 123 });
check(res, {
'status OK': (r) => r.status === grpc.StatusOK,
'ada nama': (r) => r.message.nama !== undefined,
});
client.close(); // JANGAN lupa, kalau tidak koneksi bocor
}Browser Testing dengan k6/browser
Menjalankan Chromium sungguhan untuk mengukur pengalaman pengguna di sisi frontend: waktu render, interaksi, dan Core Web Vitals.
import { browser } from 'k6/browser';
import { check } from 'k6';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
vus: 1,
iterations: 5,
options: { browser: { type: 'chromium' } },
},
},
thresholds: {
browser_web_vital_lcp: ['p(95)<2500'], // Largest Contentful Paint
browser_web_vital_cls: ['p(95)<0.1'], // Cumulative Layout Shift
browser_web_vital_inp: ['p(95)<200'], // Interaction to Next Paint
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://contoh.id/produk', { waitUntil: 'networkidle' });
await page.locator('input[name="cari"]').type('sepatu');
await Promise.all([
page.waitForNavigation(),
page.locator('button[type="submit"]').click(),
]);
const jumlah = await page.locator('.kartu-produk').count();
check(jumlah, { 'ada hasil pencarian': (n) => n > 0 });
await page.screenshot({ path: 'hasil.png' });
} finally {
await page.close(); // WAJIB, kalau tidak proses Chromium menumpuk
}
}Satu instance Chromium memakan ± 100–300 MB RAM dan satu inti CPU. Menjalankan 100 VU browser butuh mesin dengan puluhan GB RAM dan puluhan core — biaya yang tidak masuk akal.
Pola yang benar (hybrid): gunakan protokol HTTP untuk menghasilkan beban besar (ribuan VU), dan jalankan 1–5 VU browser secara paralel hanya untuk mengukur pengalaman frontend di bawah beban tersebut.
Pola Hybrid: Beban Protokol + Pengukuran Browser
export const options = {
scenarios: {
beban_protokol: {
executor: 'constant-arrival-rate',
rate: 200, timeUnit: '1s', duration: '10m',
preAllocatedVUs: 300, maxVUs: 600,
exec: 'bebanApi',
},
ukur_frontend: {
executor: 'constant-vus',
vus: 2, // cukup 2 — ini pengukur, bukan penghasil beban
duration: '10m',
options: { browser: { type: 'chromium' } },
exec: 'ukurUi',
},
},
};