WebSockets
概览
WebSocket 是一种在单个 TCP 连接上提供全双工通信通道的协议。它常用于单页应用 (SPA) 和移动应用,以添加基于服务器推送的功能,这通常可以改善基于轮询解决方案的性能。
使用 k6 进行 WebSockets 负载测试
注意
xk6-websockets 是一个实验性模块,其 API 比
k6/ws
更标准。它实现了 WebSockets API 最新标准。尽管实现尚未完成,但它使用全局事件循环而非本地事件循环。目前,它作为实验性模块
k6/experimental/websockets
提供。未来它也很可能成为 k6 核心的一部分。
将基于 HTTP 的测试与基于 WebSocket 的测试进行比较,您会发现它们在结构和内部工作原理上都存在差异。主要区别在于,每个 VU 现在运行一个异步事件循环,而不是重复地连续循环主函数 (export default function() { ... }
) 。
WebSocket 测试的基本结构如下所示
import ws from 'k6/ws';
import { check } from 'k6';
export default function () {
const url = 'ws://echo.websocket.org';
const params = { tags: { my_tag: 'hello' } };
const res = ws.connect(url, params, function (socket) {
socket.on('open', () => console.log('connected'));
socket.on('message', (data) => console.log('Message received: ', data));
socket.on('close', () => console.log('disconnected'));
});
check(res, { 'status is 101': (r) => r && r.status === 101 });
}
在此示例中,connect() 方法将一个“运行”函数作为其第三个参数。该函数应接受一个 Socket 对象作为其唯一参数。运行函数构成了异步事件循环的基础。
创建 WebSocket 连接后,运行函数将立即被调用,其中的所有代码都将执行(通常是设置事件处理程序的代码),然后阻塞,直到 WebSocket 连接关闭(由远程主机关闭或使用 socket.close() 关闭)。
错误处理
要捕获 WebSocket 连接生命周期中发生的错误,请为“error”事件附加处理程序
import ws from 'k6/ws';
import { check } from 'k6';
export default function () {
const url = 'ws://echo.websocket.org';
const params = { tags: { my_tag: 'hello' } };
const res = ws.connect(url, params, function (socket) {
socket.on('open', function open() {
// ...
});
socket.on('error', function (e) {
if (e.error() != 'websocket: close sent') {
console.log('An unexpected error occured: ', e.error());
}
});
});
check(res, { 'status is 101': (r) => r && r.status === 101 });
}
定时器
要调度重复操作,请使用 socket.setInterval 指定在特定间隔调用的函数。
import ws from 'k6/ws';
import { check } from 'k6';
export default function () {
const url = 'ws://echo.websocket.org';
const params = { tags: { my_tag: 'hello' } };
const res = ws.connect(url, params, function (socket) {
socket.on('open', function open() {
console.log('connected');
socket.setInterval(function timeout() {
socket.ping();
console.log('Pinging every 1sec (setInterval test)');
}, 1000);
});
socket.on('ping', () => console.log('PING!'));
socket.on('pong', () => console.log('PONG!'));
socket.on('close', () => console.log('disconnected'));
});
check(res, { 'status is 101': (r) => r && r.status === 101 });
}
超时
要为 WebSocket 连接添加超时,请将处理程序函数和超时值(以毫秒为单位)都传递给 socket.setTimeout 函数。
import ws from 'k6/ws';
import { check } from 'k6';
export default function () {
const url = 'ws://echo.websocket.org';
const params = { tags: { my_tag: 'hello' } };
const res = ws.connect(url, params, function (socket) {
socket.on('open', () => console.log('connected'));
socket.on('close', () => console.log('disconnected'));
socket.setTimeout(function () {
console.log('2 seconds passed, closing the socket');
socket.close();
}, 2000);
});
check(res, { 'status is 101': (r) => r && r.status === 101 });
}
在前面的示例中,超时将在 2 秒后关闭 WebSocket 连接。
多个事件处理程序
您可以为同一事件附加多个处理程序函数
import ws from 'k6/ws';
import { check } from 'k6';
export default function () {
const url = 'ws://echo.websocket.org';
const params = { tags: { my_tag: 'hello' } };
const response = ws.connect(url, params, function (socket) {
socket.on('open', function open() {
console.log('connected');
socket.send(Date.now());
socket.setInterval(function timeout() {
socket.ping();
console.log('Pinging every 1sec (setInterval test)');
}, 1000);
});
socket.on('ping', () => console.log('PING!'));
socket.on('pong', () => console.log('PONG!'));
socket.on('pong', () => {
// Multiple event handlers on the same event
console.log('OTHER PONG!');
});
socket.on('close', () => console.log('disconnected'));
socket.on('error', (e) => {
if (e.error() != 'websocket: close sent') {
console.log('An unexpected error occurred: ', e.error());
}
});
socket.setTimeout(function () {
console.log('2 seconds passed, closing the socket');
socket.close();
}, 2000);
});
check(response, { 'status is 101': (r) => r && r.status === 101 });
}