基本概念
Socket.IO
Socket.IO 是一个面向 Web 端即时通信技术的代码工具库,它主要基于 WebSocket 协议建立连接,同时也把 HTTP 长轮询 作为后备方案,支持即时、双向、基于事件的通信。其主要特性如下:
高性能:在大多数情况下,它使用 WebSocket 协议建立连接,在客户端和服务器之间,提供双向、低延迟的通信通道。
可靠性:在无法建立 WebSocket 连接时(例如浏览器不支持 WebSocket 协议、或 WebSocket 连接被代理或防火墙拦截等),它将使用 HTTP 长轮询作为替代方案。另外,如果连接丢失,客户端将自动重试连接。
可扩展:支持将应用程序部署到多个服务器,向所有连接的客户端发送事件。
WebSocket
WebSocket 是一种应用层通信协议,可在单个 TCP 连接上进行全双工通信。
不同于 HTTP 协议的客户端发起请求、服务端响应的一问一答模式,WebSocket 连接一旦建立,直到连接关闭之前,客户端、服务器之间都可源源不断地、双向地交换数据。
HTTP 长轮询
HTTP 长轮询是一种基于 HTTP 协议的 Web 端即时通信技术。当服务器收到客户端发来的请求后,不会直接进行响应,而是先将这个请求挂起,判断服务器端数据是否有更新:
如果有数据更新,则服务器正常返回响应。
如果一直没有数据更新,则达到服务器端设置的超时时间后返回。
客户端则会在处理完服务器返回的响应后,再次发出请求,重新建立连接。
HTTP 长轮询和 HTTP 短轮询相比较,HTTP 长轮询在无数据更新的情况下,不会频繁发送请求,减少了不必要的请求次数、节约了资源。
Socket.IO 压测介绍
在 PTS 压测场景中,基于 Socket.IO 请求的脚本与基于 HTTP 请求的脚本,其结构和作用机制有所不同:
执行 HTTP 脚本的每个 VU 会持续不断地迭代主函数 (
export default function() { ... }
),直到压测结束。执行 Socket.IO 脚本的每个 VU 不会持续迭代主函数,因为主函数会被建立连接的
io.connect
方法阻塞,直到连接关闭。而在连接建立后的回调函数里(function (socket) {...}
),会持续不断地监听和处理异步事件,直到压测结束。脚本编写
PTS API 的 Socket.IO 模块提供了 Socket.IO 协议的相关接口,详情可参见 socketio API。
使用这些接口,您可以建立 Socket.IO 连接、发送/收取事件消息。
基本用法:
用
connect
方法建立连接,并在其回调函数里定义您的业务逻辑:该方法的必传参数为待连接服务的 URL,和连接成功后需执行的回调函数。
该方法的可选参数为调整配置的选项,包括:
protocol
:协议类型,支持 polling(HTTP 长轮询)和 websocket(WebSocket)。headers
:请求头参数。若连接建立成功,PTS 会将创建好的
SocketIO
对象传入回调函数。您可在回调函数里,定义您的 Socket.IO 请求逻辑,发送/收取事件消息。执行完回调函数,
connect
会返回 Response
对象,200 为成功返回码。SocketIO
对象的常用方法:emit
:发送事件。参数为事件名、消息数据、回调函数:event
:自定义事件的名称。msg
:文本消息或二进制数据。callback
: (可选)回调函数。close
:关闭连接。on
:监听事件,并用回调函数处理事件。PTS 支持监听的事件列表如下:事件名 | 事件用途 |
open | 建立连接 |
close | 关闭连接 |
error | 发生错误 |
message | 接收文本消息 |
binaryMessage | 接收二进制消息 |
setTimeout
: 等待 intervalMs 毫秒后执行函数。setInterval
:按照 intervalMs 毫秒定期执行函数。setLoop
:循环执行函数直至 context 结束或者连接关闭。代码示例如下:
// SocketIO APIimport socketio from 'pts/socketio';import { check, sleep } from 'pts';import util from 'pts/util';export default function () {const res = socketio.connect('http://localhost:8080', function (socket) {socket.on('open', () => console.log('connected'));socket.on('message', (data) => console.log('message received: ', data));socket.on('binaryMessage', (data) => console.log('binaryMessage received: ', data));socket.on('close', () => console.log('disconnected'));socket.setTimeout(function () {console.log('3 seconds passed, closing the socket');socket.close();}, 3000);// 设置定时任务socket.setTimeout(function () {socket.emit('message', 'hello');socket.emit('binaryMessage', util.base64Decoding('aGVsbG8=', 'std', 'b'));socket.emit('ackMessage', 'hello ack', function(msg) {console.log('ack message received: ', msg)})}, 500);// 设置定期执行的任务socket.setInterval(function(){socket.emit('message', 'interval message');}, 500);// 设置循环执行任务,socket/context 关闭自然退出socket.setLoop(function () {sleep(0.1);socket.emit('message', 'loop message');});}, {// 支持 polling、websocket 协议protocol:'websocket',headers: {token: 'xxx',}});check('status is 200', () => res.status === 200);}
脚本验证
文件依赖
在压测场景里,您可上传以下几种类型的文件,提供压测执行时的状态数据:
参数文件:以 csv 文件的形式,动态提供测试数据。也即,场景被每个并发用户(VU)执行时,会获取参数文件里的每行数据,作为测试数据的值,供脚本里的变量引用。具体使用方法参见:使用参数文件。
请求文件:构建您的请求所需的文件,如需要上传的文件。具体使用方法参见:使用请求文件。
协议文件:请求序列化所需要用到的文件。具体使用方法参见:使用协议文件。