No results for

Powered byAlgolia

WebSockets

Overview

WebSocket is a protocol that provides full-duplex communication channels over a single TCP connection. It is commonly used by single-page apps (SPAs) and mobile apps, to add server-push based functionality, which usually improves performance in polling-based solutions.

Load testing WebSockets with k6

An extension with much better and standard API exists

xk6-websockets implements the WebSockets API living standard. While the implementation isn't full, it uses a global event loop instead of local one.

Currently, it's available as an experimental module k6/experimental/websockets. It is also likely that it will at some point become part of the core of k6.

Comparing HTTP-based tests to WebSocket ones, you'll find differences in both structure and inner workings. The primary difference is that instead of continuously looping the main function (export default function() { ... }) over and over, each VU is now runs an asynchronous event loop.

The basic structure of a WebSocket test looks like this:

Basic structure of WebSocket-based tests
1import ws from 'k6/ws';
2import { check } from 'k6';
3
4export default function () {
5 const url = 'ws://echo.websocket.org';
6 const params = { tags: { my_tag: 'hello' } };
7
8 const res = ws.connect(url, params, function (socket) {
9 socket.on('open', () => console.log('connected'));
10 socket.on('message', (data) => console.log('Message received: ', data));
11 socket.on('close', () => console.log('disconnected'));
12 });
13
14 check(res, { 'status is 101': (r) => r && r.status === 101 });
15}

In this example, the connect() method takes a "run" function as its third parameter. That function should accept a Socket object as its only parameter. The run function forms the basis of the asynchronous event loop.

When the WebSocket connection is created, the run function will be immediately called, all code inside it will be executed (usually code to set up event handlers), and then blocked until the WebSocket connection is closed (by the remote host or by using socket.close()).

Error handling

To catch errors happen during the life of a WebSocket connection, attach a handler to the "error" event:

Error handling in WebSocket tests
1import ws from 'k6/ws';
2import { check } from 'k6';
3
4export default function () {
5 const url = 'ws://echo.websocket.org';
6 const params = { tags: { my_tag: 'hello' } };
7
8 const res = ws.connect(url, params, function (socket) {
9 socket.on('open', function open() {
10 // ...
11 });
12
13 socket.on('error', function (e) {
14 if (e.error() != 'websocket: close sent') {
15 console.log('An unexpected error occured: ', e.error());
16 }
17 });
18 });
19
20 check(res, { 'status is 101': (r) => r && r.status === 101 });
21}

Timers

To schedule a recurring action, use the socket.setInterval to specify a function to call at a particular interval.

Timers in WebSocket tests
1import ws from 'k6/ws';
2import { check } from 'k6';
3
4export default function () {
5 const url = 'ws://echo.websocket.org';
6 const params = { tags: { my_tag: 'hello' } };
7
8 const res = ws.connect(url, params, function (socket) {
9 socket.on('open', function open() {
10 console.log('connected');
11
12 socket.setInterval(function timeout() {
13 socket.ping();
14 console.log('Pinging every 1sec (setInterval test)');
15 }, 1000);
16 });
17
18 socket.on('ping', () => console.log('PING!'));
19 socket.on('pong', () => console.log('PONG!'));
20 socket.on('close', () => console.log('disconnected'));
21 });
22
23 check(res, { 'status is 101': (r) => r && r.status === 101 });
24}

Timeouts

To add a timeout to the WebSocket connection, pass both a handler function and a timeout value (in milliseconds) to the socket.setTimeout function.

Timeouts in WebSocket tests
1import ws from 'k6/ws';
2import { check } from 'k6';
3
4export default function () {
5 const url = 'ws://echo.websocket.org';
6 const params = { tags: { my_tag: 'hello' } };
7
8 const res = ws.connect(url, params, function (socket) {
9 socket.on('open', () => console.log('connected'));
10 socket.on('close', () => console.log('disconnected'));
11
12 socket.setTimeout(function () {
13 console.log('2 seconds passed, closing the socket');
14 socket.close();
15 }, 2000);
16 });
17
18 check(res, { 'status is 101': (r) => r && r.status === 101 });
19}

In the preceding example, the timeout will close the WebSocket connection after 2 seconds.

Multiple event handlers

You can attach multiple handler functions to an event:

Multiple event handlers in WebSocket tests
1import ws from 'k6/ws';
2import { check } from 'k6';
3
4export default function () {
5 const url = 'ws://echo.websocket.org';
6 const params = { tags: { my_tag: 'hello' } };
7
8 const response = ws.connect(url, params, function (socket) {
9 socket.on('open', function open() {
10 console.log('connected');
11 socket.send(Date.now());
12
13 socket.setInterval(function timeout() {
14 socket.ping();
15 console.log('Pinging every 1sec (setInterval test)');
16 }, 1000);
17 });
18
19 socket.on('ping', () => console.log('PING!'));
20 socket.on('pong', () => console.log('PONG!'));
21 socket.on('pong', () => {
22 // Multiple event handlers on the same event
23 console.log('OTHER PONG!');
24 });
25
26 socket.on('close', () => console.log('disconnected'));
27
28 socket.on('error', (e) => {
29 if (e.error() != 'websocket: close sent') {
30 console.log('An unexpected error occurred: ', e.error());
31 }
32 });
33
34 socket.setTimeout(function () {
35 console.log('2 seconds passed, closing the socket');
36 socket.close();
37 }, 2000);
38 });
39
40 check(response, { 'status is 101': (r) => r && r.status === 101 });
41}