Connection Lifecycle¶
WebMQ handles the hard parts of real-time connections so you don't have to.
Connection states¶
Disconnected ──connect()──> Connecting ──open──> Connected
↑ │
│ forceReconnect() │ disconnect()
│ or network loss │ or close()
└───────────────────────┘
Reconnecting
Auto-reconnection¶
When a WebSocket disconnects unexpectedly, the client automatically retries with capped exponential backoff:
const client = new WebMQClient({
url: 'ws://localhost:8080',
sessionId: 'my-session',
reconnectDelays: [0, 500, 1000, 2000, 3000] // ms, last value repeats
});
Default delays: [0, 500, 1000, 2000, 3000] — first retry immediate, then 0.5s, 1s, 2s, then every 3s indefinitely.
Heartbeat & half-open detection¶
TCP connections can enter a half-open state where neither side detects the failure. WebMQ prevents this with a two-layer heartbeat:
Client (application-level ping/pong):
- Sends a ping message every
pingIntervalms (default 5000) - If no pong received within
pongTimeoutms (default 10000), forces reconnect client.pingIntervalandclient.pongTimeoutare configurable
Server (WebSocket ping/pong):
- Sets
isAlive = falseon every WebSocket, then sendsws.ping() - If no
pongresponse withinwsPingIntervalms (default 15000), terminates the socket wsPingIntervalis configurable inWebMQServeroptions
const server = new WebMQServer({
rmqUrl: 'amqp://localhost',
exchange: 'my_app',
port: 8080,
wsPingInterval: 10000 // Check every 10s, terminate stale after 10s
});
Offline queuing¶
Messages published while disconnected are queued in memory on the client. On reconnect:
- The client re-sends all queued messages with their original
messageId - The server processes them normally
- The publish promises resolve or reject based on server ACK
This means your application code can publish() without worrying about connection state — the message either goes through immediately or gets queued and sent on reconnect.
// This works even while disconnected
client.publish('orders.create', { item: 'widget' });
// Promise resolves when the server ACKs (now or after reconnect)
Session dedup¶
When a client reconnects with the same sessionId, the server:
- Cancels the old consumer on RabbitMQ (stops delivering messages to the dead connection)
- Terminates the old WebSocket
- Creates a new session queue consumer for the new connection
This prevents duplicate message delivery when a client reconnects before the server detects the old connection is dead.
Browser lifecycle integration¶
WebMQ hooks into browser events for smarter reconnection:
window.online— when the browser regains network connectivity, WebMQ forces an immediate reconnectvisibilitychange— when a tab becomes visible again (e.g., user switches back), WebMQ checks connection health and reconnects if needed
This is especially useful for mobile web apps where network connectivity is intermittent and tabs may be backgrounded for extended periods.
Graceful shutdown¶
Call client.disconnect() when your component unmounts or the user logs out:
// Clean disconnect — server deletes the session queue
client.disconnect();
// Or for the server
await server.stop(); // Closes all WebSockets, cancels consumers, closes RMQ
The server registers SIGTERM/SIGINT handlers that call stop() on all active WebMQServer instances.
Session queues are only deleted on a clean client disconnect. When the server shuts down (or is scaled down), it does not delete session queues: clients reconnect to a remaining backend and resume with the same queue, and any messages published during the gap are buffered by RabbitMQ and delivered on reconnect. Queues left behind by abandoned connections are cleaned up by the queueTimeout TTL (default 5 min) once the last consumer leaves.
Connection events¶
WebMQClient extends EventTarget and dispatches CustomEvents:
| Event | Detail | When |
|---|---|---|
connected |
— | First connection established |
disconnected |
— | Connection lost and reconnection attempts are ongoing |
reconnecting |
{ attempt } |
About to attempt a reconnection |
reconnected |
— | Connection restored after reconnection |
error |
— | WebSocket error occurred |
client.addEventListener('connected', () => console.log('Connected'));
client.addEventListener('disconnected', () => console.log('Disconnected'));
client.addEventListener('reconnecting', (e) =>
console.log(`Reconnecting... attempt ${e.detail.attempt}`));
client.addEventListener('reconnected', () => console.log('Reconnected'));
Configuring timeouts¶
All configurable via WebMQClient constructor options or properties:
| Setting | Default | Description |
|---|---|---|
pingInterval |
5000 | Client ping interval (ms) |
pongTimeout |
10000 | Max wait for pong before reconnect (ms) |
ackTimeout |
5000 | Max wait for server ACK (ms) |
reconnectDelays |
[0, 500, 1000, 2000, 3000] | Backoff sequence (ms) |
Server-side:
| Setting | Default | Description |
|---|---|---|
wsPingInterval |
15000 | Server ping interval (ms) |
clientAckTimeout |
10000 | Max wait for client ACK before requeue (ms) |
queueTimeout |
300000 | Session queue TTL (ms) |