Skip to main content
18 jun 2026pruebas carga k6 websocket

Por Performate

Pruebas de carga WebSocket con k6: conexiones, heartbeats y backpressure

Pruebas de carga WebSocket con k6: conexiones concurrentes, heartbeats, backpressure—docs API WebSocket k6 más patrones de emparejamiento REST.

Tu dashboard REST muestra p99 verde en 120 ms, pero el chat en vivo se cae a los 2 000 usuarios conectados porque nadie stress-testeó sockets concurrentes—solo el POST de login que entrega el token. El reverse proxy tiene worker_connections para 1 024; el backend asume heartbeats cada 30 s; y el reconnect replay dispara un thundering herd que tumba Redis.

El protocolo WebSocket (RFC 6455) hace upgrade de HTTP a un canal bidireccional. La carga se mide en sockets concurrentes, mensajes/seg y churn de reconexión—no solo en http_req_duration de llamadas REST. En esta guía verás dimensiones de estrés, un escenario k6 con el módulo WebSocket experimental, y cómo emparejar fases HTTP + WS con realismo.

Por qué WebSocket cambia el perfil de carga—no solo el transporte

A nivel funcional, REST y WS comparten auth. Bajo concurrencia, ejercitan recursos distintos:

  • Fan-out de conexiones — cada socket consume FD en proxy, LB y app; superar worker_connections encuentra techos antes que CPU.
  • Heartbeats + idle — ping/pong desalineados o timeouts idle del proxy crean caídas silenciosas—not errores HTTP visibles.
  • Burst tras reconnect — clientes replay suscripciones; servidores deben sobrevivir thundering herds post-deploy.
  • Backpressure — publicar más rápido de lo que el consumer procesa llena buffers—not lo captura latencia REST.

Piensa en medir un call center solo contando emails mientras 5 000 líneas telefónicas simultáneas son el cuello real.

Cuando REST-only load tests pasan pero el realtime falla

La mayoría de productos intercalan REST y sockets—login HTTP, luego feed WS. Corre escenarios mixtos para que pools de conexión y tokens interactúen con realismo (plantilla script mínimo). Etiqueta feeds lógicos con tags de observabilidad. Si server→client basta sin bidireccional, compara con SSE y long-polling.

Implementación práctica con k6: conexión, subscribe y heartbeat

Modela conexión WS tras auth REST, suscripción a un feed, ping periódico y cierre limpio. Coordina socket storms en staging compartido (ética de pruebas).

Script de ejemplo (ilustrativo—no es una prueba lista para producción). Usa API experimental de WebSocket; adapta URLs, intervals y payloads.

Qué demuestra este ejemplo:

  • Fase REST previa: obtiene token antes del upgrade WS—como clientes reales.
  • Conexión + subscribe: envía frame de suscripción tras open.
  • Heartbeat: ping cada 25 s alineado a keepalive de prod.
  • Tags por feed: feed:orders segmenta métricas en summary y APM.
import http from 'k6/http';
import { check, sleep } from 'k6';
import ws from 'k6/experimental/websockets';

const BASE_HTTP = __ENV.API_BASE || 'https://staging.example.com';
const BASE_WS = __ENV.WS_BASE || 'wss://staging.example.com';
const FEED = 'orders';

export const options = {
  scenarios: {
    ws_connections: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '2m', target: 100 },
        { duration: '5m', target: 500 },
        { duration: '2m', target: 500 },
        { duration: '1m', target: 0 },
      },
      exec: 'wsSession',
      tags: { protocol: 'websocket' },
    },
  },
  thresholds: {
    http_req_failed: ['rate<0.01'],
    ws_connecting: ['p(95)<2000'],
    ws_msgs_received: ['count>0'],
  },
};

export function wsSession() {
  const login = http.post(
    `${BASE_HTTP}/v1/auth/token`,
    JSON.stringify({ clientId: 'load-bot' }),
    { headers: { 'Content-Type': 'application/json' }, tags: { route: 'auth' } }
  );
  check(login, { 'auth ok': (r) => r.status === 200 });
  const token = login.json('accessToken');

  const url = `${BASE_WS}/v1/ws?token=${token}`;
  const res = ws.connect(url, { tags: { feed: FEED } }, (socket) => {
    socket.on('open', () => {
      socket.send(JSON.stringify({ action: 'subscribe', feed: FEED }));
    });

    socket.on('message', (data) => {
      check(data, { 'message received': (d) => d.length > 0 });
    });

    socket.setInterval(() => {
      socket.ping();
    }, 25000);

    socket.setTimeout(() => {
      socket.close();
    }, 120000);
  });

  check(res, { 'ws connected': (r) => r && r.status === 101 });
  sleep(1);
}

Patrones que funcionan

  • Ramp gradual de conexiones — encuentra worker_connections antes del cliff.
  • Keepalive = prod — replica intervalos ping/pong y proxy idle timeouts.
  • Escenario mixto REST+WS — auth, REST setup, luego socket en la misma VU.
  • Reconnect churn — escenario separado que cierra y reabre sockets en ráfaga post-ramp.

Anti-patrones a evitar

  • Medir solo REST cuando el incidente es fan-out de sockets.
  • Abrir 10 000 conexiones instantáneas en staging compartido sin ventana acordada.
  • Ignorar FD limits en el generador de carga—el benchmark miente si k6 es el cuello.

Tip pro (comando de ejemplo):

k6 run ws-load.js -e API_BASE=https://staging.example.com -e WS_BASE=wss://staging.example.com

Qué demuestra este comando: variables separadas para HTTP y WS—patrón común cuando el upgrade host difiere del API REST.

Marco de decisión: qué dimensionar primero

SituaciónAcción recomendada
Producto nuevo realtimeRamp de conexiones; encuentra techo de proxy/LB
Incidente post-deployEscenario reconnect burst + replay subscribe
Caídas silenciosas tras idleAlinear heartbeat a prod; probar timeout proxy
Gaming / trading burstsMensajes/seg + backpressure, no solo conexiones
Solo server→clientEvaluar SSE/long-polling antes de WS

Dimensiona conexiones primero si el síntoma es « too many open files » o L4 limits.

Dimensiona heartbeats si las caídas correlacionan con idle, no con CPU.

Dimensiona reconnect si deploys o network blips disparan thundering herds.

Observabilidad, documentación y siguientes pasos

Las pruebas WS solo sirven si documentas límites y coordinación. Antes de escalar sockets:

  • Documenta worker_connections, FD limits en generators y expectativas de ephemeral ports.
  • Coordina ventana con platform en staging compartido (ética).
  • Correlaciona tags feed:* en k6 con traces APM por canal lógico.
  • Registra intervalos ping/pong de prod en el README del escenario.
  • Archiva summary con ws_connecting y conteos de mensajes por release.

Cómo Performate simplifica journeys REST + WebSocket

Mantener auth REST en un script y WS en otro produce drift. Abajo hay un ejemplo concreto de flujo para feed de orders—adapta feeds y payloads a tu producto.

Ejemplo: login REST → subscribe WS → heartbeat en un workspace

  1. Importa requests REST (auth token) y define URL WS en variables de entorno. Problema resuelto: pre-step HTTP y socket en un journey visible para PM.
  2. Crea escenario ramping-vus 100→500 conexiones con stages graduales. Problema resuelto: fan-out realista sin script WS aislado.
  3. Configura payload de subscribe y tag feed:orders en el panel. Problema resuelto: segmentación alineada al ejemplo k6.
  4. Define intervalo heartbeat 25 s en el flujo visual—match prod keepalive. Problema resuelto: caídas idle detectables antes de prod.
  5. Corre y compara métricas WS vs REST en reporte integrado—detecta si auth REST es el cuello previo al upgrade. Problema resuelto: un export para backend y platform.
  6. Exporta script k6 con módulo experimental WS para nightly (CI/CD).

Ese flujo conecta directo con el cta de este post: escenarios ejecutables sin días de código de integración REST+WS.

Cierre

El riesgo realtime es un problema de conexiones y churn, no solo de latencia REST. Stress-testea sockets concurrentes, heartbeats alineados a prod y bursts de reconnect—etiquetados por feed—antes del próximo launch que promete « live » en el hero.

Corre una rampa a 500 conexiones en staging esta semana—and anota en qué stage apareció el primer timeout de proxy o FD limit.

Try Performate free | Book a demo | k6 WebSocket docs

¿Listo para optimizar el rendimiento de tu API?

Usa Performate para convertir este playbook en escenarios k6 ejecutables, umbrales y reportes compartibles sin perder días en código de integración.

← Volver a todas las entradas