| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- // WebSocket 并发压测(模拟进入直播间+心跳)
- const WebSocket = require('ws');
- const crypto = require('crypto');
- const WS_HOST = process.env.WS_HOST || 'wss://im.fhhx.runtzh.com/ws/app/webSocket';
- const LIVE_ID = process.env.LIVE_ID; // 必填:直播间ID
- const USER_TYPE = parseInt(process.env.USER_TYPE || '0', 10); // 与前端一致,默认0
- const HEARTBEAT_MS = parseInt(process.env.HB || '15000', 10); // 心跳间隔
- const RATE = parseInt(process.env.RATE || '80', 10); // 每秒建链速率
- const DURATION_SEC = parseInt(process.env.DURATION || '600', 10);// 运行总时长(秒)
- // 新增:可选 Origin 和扫码参数
- const ORIGIN = process.env.ORIGIN || 'https://im.fhhx.runtzh.com';
- const COMPANY_ID = process.env.COMPANY_ID;
- const COMPANY_USER_ID = process.env.COMPANY_USER_ID;
- const UID_START = parseInt(process.env.UID_START || '900000000', 10);
- const UID_TO = parseInt(process.env.UID_TO || '0', 10);
- let COUNT = parseInt(process.env.COUNT || '0', 10);
- if (!LIVE_ID) {
- console.error('缺少 LIVE_ID。示例:LIVE_ID=359 node scripts/ws-load-test.js');
- process.exit(1);
- }
- if (UID_TO > 0 && UID_TO >= UID_START) {
- COUNT = UID_TO - UID_START + 1;
- }
- if (!COUNT || COUNT <= 0) {
- console.error('缺少 COUNT 或 UID_TO,无法确定连接数量');
- process.exit(1);
- }
- function buildUrl(userId) {
- const ts = Date.now();
- const message = `${LIVE_ID}${userId}${USER_TYPE}${ts}`;
- const signature = crypto.createHmac('sha256', String(ts)).update(message).digest('hex');
- let url = `${WS_HOST}?userId=${userId}&liveId=${LIVE_ID}&userType=${USER_TYPE}×tamp=${ts}&signature=${signature}`;
- // 新增:可选扫码来源参数
- if (COMPANY_ID && COMPANY_USER_ID) {
- url += `&companyId=${encodeURIComponent(COMPANY_ID)}&companyUserId=${encodeURIComponent(COMPANY_USER_ID)}`;
- }
- return url;
- }
- const stats = { open: 0, close: 0, error: 0, msg: 0 };
- function startClient(index) {
- const userId = String(UID_START + index);
- const url = buildUrl(userId);
- // 新增:传入 origin 头,避免被服务端拒绝
- const ws = new WebSocket(url, { perMessageDeflate: false, origin: ORIGIN });
- let hbTimer = null;
- ws.on('open', () => {
- stats.open++;
- console.log(`[${userId}] connected`);
- const sendHb = () => {
- const now = Date.now();
- const payload = JSON.stringify({
- cmd: 'heartbeat',
- msg: 'ping',
- userId,
- liveId: LIVE_ID,
- timestamp: now,
- networkType: 'wifi',
- });
- ws.send(payload);
- };
- // 新增:打开即发送一次心跳, subsequent 定时发送
- sendHb();
- hbTimer = setInterval(sendHb, HEARTBEAT_MS);
- });
- // 新增:打印服务端返回的 cmd,观察是否有 heartbeat/entry/out 等
- ws.on('message', (msg) => {
- stats.msg++;
- try {
- const parsed = JSON.parse(msg);
- if (parsed && parsed.data && parsed.data.cmd) {
- console.log(`[${userId}] recv cmd=${parsed.data.cmd}`);
- }
- } catch (_) {}
- });
- ws.on('error', (err) => {
- stats.error++;
- console.error(`[${userId}] error`, err?.message || err);
- if (hbTimer) clearInterval(hbTimer);
- });
- ws.on('close', (code, reason) => {
- stats.close++;
- console.log(`[${userId}] closed code=${code} reason=${reason || ''}`);
- if (hbTimer) clearInterval(hbTimer);
- });
- }
- async function run() {
- console.log(`开始压测: liveId=${LIVE_ID}, count=${COUNT}, rate=${RATE}/s, hb=${HEARTBEAT_MS}ms, uid=[${UID_START}..${UID_START + COUNT - 1}]`);
- for (let i = 0; i < COUNT; i++) {
- startClient(i);
- const delayMs = Math.max(1, Math.floor(1000 / RATE));
- await new Promise((r) => setTimeout(r, delayMs));
- }
- const endAt = Date.now() + DURATION_SEC * 1000;
- const t = setInterval(() => {
- console.log(`[stats] open=${stats.open} close=${stats.close} error=${stats.error} msg=${stats.msg}`);
- if (Date.now() >= endAt) {
- console.log('压测结束。');
- clearInterval(t);
- process.exit(0);
- }
- }, 2000);
- }
- run().catch((e) => { console.error('运行异常', e); process.exit(1); });
|