# 安装 xk6
go install go.k6.io/xk6/cmd/xk6@latest
# 构建包含 webrtc 插件的 k6
xk6 build --with github.com/grafana/xk6-webrtc@latest
# 或者指定版本
xk6 build --with github.com/grafana/xk6-webrtc@v0.3.0
构建完成后会生成一个k6可执行文件。
2. 基础使用
一个基本的WebRTC测试脚本:
// basic-webrtc-test.js
import { WebRTC } from 'k6/experimental/webrtc';
import { check } from 'k6';
// 信令服务器地址
const SIGNALING_SERVER = 'ws://localhost:8080/signaling';
export default function () {
// 创建 WebRTC 配置
const client = new WebRTC({
iceServers: [
{ urls: ['stun:stun.l.google.com:19302'] },
// 如果需要 TURN 服务器
// { urls: 'turn:your-turn-server.com', username: 'user', credential: 'pass' }
],
});
// 创建模拟媒体流
const stream = client.createStream({ audio: true, video: true });
client.addStream(stream);
// 设置事件处理器
client.onconnectionstatechange = (state) => {
console.log(`Connection state changed to: ${state}`);
};
client.oniceconnectionstatechange = (state) => {
console.log(`ICE connection state changed to: ${state}`);
};
// 连接到信令服务器
const ws = new WebSocket(SIGNALING_SERVER);
ws.onopen = () => {
console.log('Connected to signaling server');
// 创建 offer
client.createOffer()
.then((offer) => {
// 设置本地描述
return client.setLocalDescription(offer);
})
.then(() => {
// 通过信令服务器发送 offer
ws.send(JSON.stringify({
type: 'offer',
sdp: client.localDescription
}));
})
.catch((err) => {
console.error('Error creating offer:', err);
});
};
// 处理来自信令服务器的消息
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'answer') {
// 收到 answer,设置远程描述
client.setRemoteDescription(message.sdp)
.then(() => {
console.log('Remote description set successfully');
})
.catch((err) => {
console.error('Error setting remote description:', err);
});
} else if (message.candidate) {
// 添加 ICE candidate
client.addIceCandidate(message.candidate)
.then(() => {
console.log('ICE candidate added');
})
.catch((err) => {
console.error('Error adding ICE candidate:', err);
});
}
};
// 检查连接状态
check(client, {
'connection established': (c) => c.connectionState === 'connected',
'ice connection successful': (c) => c.iceConnectionState === 'connected',
});
// 收集统计信息
const stats = client.getStats();
for (let stat of stats) {
console.log(`Stat: ${stat.type} - ${stat.id}: ${stat.value}`);
}
}
export function setup() {
// 测试设置,可选
console.log('Starting WebRTC load test');
}
export function teardown() {
// 测试清理,可选
console.log('WebRTC load test completed');
}
3. 高级功能
数据通道测试
// data-channel-test.js
import { WebRTC } from 'k6/experimental/webrtc';
export default function () {
const client = new WebRTC({
iceServers: [{ urls: ['stun:stun.l.google.com:19302'] }]
});
// 创建数据通道
const dataChannel = client.createDataChannel('test-channel', {
ordered: true,
maxRetransmits: 3
});
dataChannel.onopen = () => {
console.log('Data channel opened');
// 发送测试数据
for (let i = 0; i < 10; i++) {
dataChannel.send(`Test message ${i}`);
}
};
dataChannel.onmessage = (event) => {
console.log(`Received message: ${event.data}`);
};
dataChannel.onclose = () => {
console.log('Data channel closed');
};
// 其余信令逻辑与基础示例相同...
}
媒体流统计监控
// media-stats-test.js
import { WebRTC } from 'k6/experimental/webrtc';
import { Trend, Rate, Counter } from 'k6/metrics';
// 自定义指标
const audioBitrate = new Trend('webrtc_audio_bitrate_bps');
const videoBitrate = new Trend('webrtc_video_bitrate_bps');
const packetLoss = new Rate('webrtc_packet_loss_rate');
const connectionSuccess = new Counter('webrtc_connection_success');
export default function () {
const client = new WebRTC({
iceServers: [{ urls: ['stun:stun.l.google.com:19302'] }]
});
// 创建媒体流
const stream = client.createStream({
audio: {
bitrate: 64000, // 64 kbps
codec: 'OPUS'
},
video: {
bitrate: 500000, // 500 kbps
codec: 'VP8'
}
});
client.addStream(stream);
// 定期收集统计信息
const interval = setInterval(() => {
const stats = client.getStats();
stats.forEach(stat => {
switch(stat.type) {
case 'outbound-rtp':
if (stat.kind === 'audio') {
audioBitrate.add(stat.bitrate || 0);
} else if (stat.kind === 'video') {
videoBitrate.add(stat.bitrate || 0);
}
break;
case 'candidate-pair':
if (stat.state === 'succeeded') {
connectionSuccess.add(1);
}
break;
}
});
}, 1000); // 每秒收集一次
// 测试完成后清理
setTimeout(() => {
clearInterval(interval);
client.close();
}, 30000); // 运行30秒
}
4. 运行测试
# 运行基础测试
./k6 run basic-webrtc-test.js
# 带虚拟用户的负载测试
./k6 run --vus 10 --duration 30s basic-webrtc-test.js
# 分段测试
./k6 run --stages 30s:10,1m:20,30s:10 basic-webrtc-test.js
# 输出详细结果
./k6 run --summary-export=results.json basic-webrtc-test.js
5. 配置选项
WebRTC配置
const client = new WebRTC({
// ICE 服务器配置
iceServers: [
{ urls: 'stun:stun.zmtests.com:19302' },
{
urls: 'turn:turn.zmtests.com:3478',
username: 'user',
credential: 'password'
}
],
// ICE 传输策略
iceTransportPolicy: 'all', // 'relay' | 'all'
// Bundle 策略
bundlePolicy: 'balanced', // 'balanced' | 'max-compat' | 'max-bundle'
// RTCP mux 策略
rtcpMuxPolicy: 'require', // 'require' | 'negotiate'
// 证书
certificates: [] // 可选的证书数组
});
媒体流配置
const stream = client.createStream({
audio: {
bitrate: 128000, // 比特率 (bps)
codec: 'OPUS', // 编码器
sampleRate: 48000, // 采样率
channelCount: 2 // 声道数
},
video: {
bitrate: 1000000, // 比特率 (bps)
codec: 'VP8', // 编码器
width: 1280, // 宽度
height: 720, // 高度
frameRate: 30 // 帧率
}
});
6. 注意事项
信令服务器:需要单独实现信令服务器来处理SDP和ICE candidate交换
媒体服务器:需要真实的媒体服务器(如 Mediasoup, Jitsi, Pion)来处理媒体流
资源限制:大量WebRTC连接会消耗大量CPU和网络资源
网络模拟:可以使用 xk6-net 插件模拟网络条件
错误处理:务必添加适当的错误处理,因为WebRTC连接可能因为各种原因失败
这个插件为WebRTC应用的负载测试提供了强大的测试能力,特别适合测试信令服务器和基础连接性能。