Files
dsh_shenxian/scripts/overlay-jitter.cjs
T
admin 146c3d25ef feat(overlay): 覆盖网络线序①–⑮ 代码与测试产物入库
覆盖网络线累积产物(此前只在工作区、未入版本库):
- 新增 relay 子系统 src/net/relay/**(wire/duplex/server/client/dialer/switcher/directory/identity/keys/placement/network/addr-override/main/index)
- 新增 src/worker/relay-tunnel.ts、src/web/routes/overlay.ts
- 新增观测/演练脚本 overlay-probe、overlay-failover-drill、overlay-keyring、overlay-holepunch、overlay-jitter、overlay-wan、overlay-relaykey-add、relay-mem-calibrate
- 新增测试 12 个(relay / relay-failover / remote-spawner / instance-port / overlay-{network,auth,bootstrap,identity} / remote-user-fs 等)

验收基线:npm test = 162 pass / 0 fail / 1 skip;--scene all = 12 PASS / 0 SKIP / 0 FAIL;overlay-probe = 12/12
2026-09-17 17:03:27 +08:00

126 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* 覆盖网络 序⑥ · S3 链路抖动与 RTT 口径校验(⛔ 一次性脚本、不进产品路径)。
*
* ## 为什么需要它
* 参数表把 `RELAY_RTT_W106 = 336 ms` 标成"实测",但它其实来自 `server.ts:810` 的
* **心跳往返**(一次 WS 往返 + 应用层处理 + 验签)⇒ **不一定等于网络 RTT**。
* 而 336 ms 目前是"跨云链路很差"的**唯一证据** ⇒ 若它是口径问题,后面所有
* "跨云不可玩"的结论都要重判。本脚本做**三方对比**:ICMP / TCP 握手 / relay 心跳。
*
* 用法:
* node overlay-jitter.cjs --icmp 106.54.21.172 --count 300 --interval 0.2
* node overlay-jitter.cjs --tcp 106.54.21.172:22 --tcp-n 30
* node overlay-jitter.cjs --icmp H --count 300 --tcp H:22 --tcp-n 30 # 两者一起跑
*
* @module scripts/overlay-jitter
*/
'use strict'
const net = require('node:net')
const { spawnSync } = require('node:child_process')
function parseArgs(argv) {
const out = {}
for (let i = 0; i < argv.length; i++) {
const a = argv[i]
if (!a.startsWith('--')) continue
const k = a.slice(2)
const v = argv[i + 1]
if (v === undefined || v.startsWith('--')) out[k] = true
else {
out[k] = isNaN(Number(v)) || v === '' ? v : Number(v)
i++
}
}
return out
}
const args = parseArgs(process.argv.slice(2))
const log = (s) => process.stderr.write(`[jit] ${s}\n`)
const out = (o) => process.stdout.write(`### RESULT ### ${JSON.stringify(o)}\n`)
function pct(arr, p) {
if (arr.length === 0) return 0
const s = [...arr].sort((a, b) => a - b)
return Math.round(s[Math.min(s.length - 1, Math.floor(s.length * p))] * 100) / 100
}
function icmp(host, count, interval) {
const win = process.platform === 'win32'
const argv = win
? ['-n', String(count), '-w', '1000', host]
: ['-c', String(count), '-i', String(interval || 0.2), '-W', '1', host]
const r = spawnSync(win ? 'ping' : 'ping', argv, { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 })
const text = `${r.stdout || ''}`
const rtts = []
const re = win ? /time[=<]([\d.]+)\s*ms/gi : /time=([\d.]+)\s*ms/gi
let m
while ((m = re.exec(text)) !== null) rtts.push(Number(m[1]))
if (rtts.length < 2) return { host, packets: rtts.length, error: 'no-rtt-samples', raw: text.slice(0, 300) }
const deltas = []
for (let i = 1; i < rtts.length; i++) deltas.push(Math.abs(rtts[i] - rtts[i - 1]))
const avg = rtts.reduce((a, b) => a + b, 0) / rtts.length
const mdev = Math.sqrt(rtts.reduce((a, b) => a + (b - avg) ** 2, 0) / rtts.length)
return {
host,
packets: rtts.length,
sentExpected: count,
lossPct: Math.round(((count - rtts.length) / count) * 10000) / 100,
min: pct(rtts, 0),
avg: Math.round(avg * 100) / 100,
p50: pct(rtts, 0.5),
max: pct(rtts, 1),
mdev: Math.round(mdev * 100) / 100,
p95AbsDelta: pct(deltas, 0.95),
p50AbsDelta: pct(deltas, 0.5),
note_win: win ? 'windows ping:无 -i 间隔参数,实际约 1 包/秒' : undefined,
}
}
function tcpHandshake(host, port, n) {
const rtts = []
let left = n
return new Promise((resolve) => {
const one = () => {
const t = process.hrtime.bigint()
const s = net.connect(port, host)
const done = (ok) => {
if (ok) rtts.push(Number(process.hrtime.bigint() - t) / 1e6)
s.destroy()
if (--left <= 0) {
resolve({
target: `${host}:${port}`,
samples: rtts.length,
min: pct(rtts, 0),
median: pct(rtts, 0.5),
p95: pct(rtts, 0.95),
max: pct(rtts, 1),
})
return
}
setTimeout(one, 100)
}
s.setTimeout(5000)
s.on('connect', () => done(true))
s.on('error', () => done(false))
s.on('timeout', () => done(false))
}
one()
})
}
async function main() {
const res = { host: require('node:os').hostname(), platform: process.platform }
if (args.icmp) res.icmp = icmp(String(args.icmp), args.count || 300, args.interval)
if (args.tcp) {
const [h, p] = String(args.tcp).split(':')
res.tcpHandshake = await tcpHandshake(h, Number(p || 22), args['tcp-n'] || 30)
}
out(res)
process.exit(0)
}
main()