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
This commit is contained in:
1 parent
640813e84e
commit
146c3d25ef
53 files changed
+16187
-63
No files matched your search
@@ -0,0 +1,125 @@
|
||||
#!/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()
|
||||
Reference in new issue
Block a user